blob: 2f1c62fcfd1279ee8d6bd4e93b13b3604eeea474 [file] [log] [blame]
From 74eaf4cb9dfdfc0deab5c9a1b8e3de82916ca94b Mon Sep 17 00:00:00 2001
From: Soren Brinkmann <soren.brinkmann@xilinx.com>
Date: Tue, 24 Dec 2013 09:18:18 +0900
Subject: mtd: xilinx: merge nand flash support from xilinx repository
This merges support for the Xilinx nand flash support from the
Xilinx repository (commit efc27505715e64526653f35274717c0fc56491e3
in master branch). It has been tested by reading the QSPI flash
in the Zynq 702 board.
Signed-off-by: Daniel Sangorrin <daniel.sangorrin@toshiba.co.jp>
Signed-off-by: Yoshitake Kobayashi <yoshitake.kobayashi@toshiba.co.jp>
---
drivers/mtd/devices/m25p80.c | 417 +++++++++++++--
drivers/mtd/nand/Kconfig | 7
drivers/mtd/nand/Makefile | 1
drivers/mtd/nand/nand_base.c | 28 +
drivers/mtd/nand/xilinx_nandps.c | 1064 +++++++++++++++++++++++++++++++++++++++
5 files changed, 1469 insertions(+), 48 deletions(-)
create mode 100644 drivers/mtd/nand/xilinx_nandps.c
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -41,12 +41,16 @@
#define OPCODE_WRSR 0x01 /* Write status register 1 byte */
#define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
+#define OPCODE_QUAD_READ 0x6b /* Quad read command */
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
+#define OPCODE_QPP 0x32 /* Quad page program */
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
#define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
#define OPCODE_RDID 0x9f /* Read JEDEC ID */
+#define OPCODE_RDFSR 0x70 /* Read Flag Status Register */
+#define OPCODE_WREAR 0xc5 /* Write Extended Address Register */
/* Used for SST flashes only. */
#define OPCODE_BP 0x02 /* Byte program */
@@ -59,6 +63,7 @@
/* Used for Spansion flashes only. */
#define OPCODE_BRWR 0x17 /* Bank register write */
+#define OPCODE_BRRD 0x16 /* Bank register read */
/* Status Register bits. */
#define SR_WIP 1 /* Write in progress */
@@ -69,8 +74,11 @@
#define SR_BP2 0x10 /* Block protect 2 */
#define SR_SRWD 0x80 /* SR write protect */
+/* Flag Status Register bits. */
+#define FSR_RDY 0x80 /* Ready/Busy program erase
+ controller */
/* Define max times to check status register before we give up. */
-#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
+#define MAX_READY_WAIT_JIFFIES (480 * HZ) /* N25Q specs 480s max chip erase */
#define MAX_CMD_SIZE 5
#define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16)
@@ -86,6 +94,15 @@ struct m25p {
u8 erase_opcode;
u8 *command;
bool fast_read;
+ u16 curbank;
+ u32 jedec_id;
+ bool check_fsr;
+ bool shift;
+ bool isparallel;
+ bool isstacked;
+ u8 read_opcode;
+ u8 prog_opcode;
+ u8 dummycount;
};
static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
@@ -100,21 +117,19 @@ static inline struct m25p *mtd_to_m25p(s
*/
/*
- * Read the status register, returning its value in the location
- * Return the status register value.
+ * Read register, returning its value in the location
* Returns negative if error occurred.
*/
-static int read_sr(struct m25p *flash)
+static inline int read_spi_reg(struct m25p *flash, u8 code, const char *name)
{
ssize_t retval;
- u8 code = OPCODE_RDSR;
u8 val;
retval = spi_write_then_read(flash->spi, &code, 1, &val, 1);
if (retval < 0) {
- dev_err(&flash->spi->dev, "error %d reading SR\n",
- (int) retval);
+ dev_err(&flash->spi->dev, "error %d reading %s\n",
+ (int) retval, name);
return retval;
}
@@ -122,6 +137,26 @@ static int read_sr(struct m25p *flash)
}
/*
+ * Read flag status register, returning its value in the location
+ * Return flag status register value.
+ * Returns negative if error occurred.
+ */
+static int read_fsr(struct m25p *flash)
+{
+ return read_spi_reg(flash, OPCODE_RDFSR, "FSR");
+}
+
+/*
+ * Read the status register, returning its value in the location
+ * Return the status register value.
+ * Returns negative if error occurred.
+ */
+static int read_sr(struct m25p *flash)
+{
+ return read_spi_reg(flash, OPCODE_RDSR, "SR");
+}
+
+/*
* Write status register 1 byte
* Returns negative if error occurred.
*/
@@ -159,6 +194,9 @@ static inline int write_disable(struct m
*/
static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable)
{
+ int ret;
+ u8 val;
+
switch (JEDEC_MFR(jedec_id)) {
case CFI_MFR_MACRONIX:
case 0xEF /* winbond */:
@@ -168,7 +206,19 @@ static inline int set_4byte(struct m25p
/* Spansion style */
flash->command[0] = OPCODE_BRWR;
flash->command[1] = enable << 7;
- return spi_write(flash->spi, flash->command, 2);
+ ret = spi_write(flash->spi, flash->command, 2);
+
+ /* verify the 4 byte mode is enabled */
+ flash->command[0] = OPCODE_BRRD;
+ spi_write_then_read(flash->spi, flash->command, 1, &val, 1);
+ if (val != enable << 7) {
+ dev_warn(&flash->spi->dev,
+ "fallback to 3-byte address mode\n");
+ dev_warn(&flash->spi->dev,
+ "maximum accessible size is 16MB\n");
+ flash->addr_width = 3;
+ }
+ return ret;
}
}
@@ -179,15 +229,21 @@ static inline int set_4byte(struct m25p
static int wait_till_ready(struct m25p *flash)
{
unsigned long deadline;
- int sr;
+ int sr, fsr;
deadline = jiffies + MAX_READY_WAIT_JIFFIES;
do {
if ((sr = read_sr(flash)) < 0)
break;
- else if (!(sr & SR_WIP))
+ else if (!(sr & SR_WIP)) {
+ if (flash->check_fsr) {
+ fsr = read_fsr(flash);
+ if (!(fsr & FSR_RDY))
+ return 1;
+ }
return 0;
+ }
cond_resched();
@@ -197,6 +253,48 @@ static int wait_till_ready(struct m25p *
}
/*
+ * Update Extended Address/bank selection Register.
+ * Call with flash->lock locked.
+ */
+static int write_ear(struct m25p *flash, u32 addr)
+{
+ u8 ear;
+ int ret;
+
+ /* Wait until finished previous write command. */
+ if (wait_till_ready(flash))
+ return 1;
+
+ if (flash->mtd.size <= (0x1000000) << flash->shift)
+ return 0;
+
+ addr = addr % (u32) flash->mtd.size;
+ ear = addr >> 24;
+
+ if ((!flash->isstacked) && (ear == flash->curbank))
+ return 0;
+
+ if (flash->isstacked && (flash->mtd.size <= 0x2000000))
+ return 0;
+
+ if (JEDEC_MFR(flash->jedec_id) == 0x01)
+ flash->command[0] = OPCODE_BRWR;
+ if (JEDEC_MFR(flash->jedec_id) == 0x20) {
+ write_enable(flash);
+ flash->command[0] = OPCODE_WREAR;
+ }
+ flash->command[1] = ear;
+
+ ret = spi_write(flash->spi, flash->command, 2);
+ if (ret)
+ return ret;
+
+ flash->curbank = ear;
+
+ return 0;
+}
+
+/*
* Erase the whole flash memory
*
* Returns 0 if successful, non-zero otherwise.
@@ -210,6 +308,9 @@ static int erase_chip(struct m25p *flash
if (wait_till_ready(flash))
return 1;
+ if (flash->isstacked)
+ flash->spi->master->flags &= ~SPI_MASTER_U_PAGE;
+
/* Send write enable, then erase commands. */
write_enable(flash);
@@ -218,16 +319,32 @@ static int erase_chip(struct m25p *flash
spi_write(flash->spi, flash->command, 1);
+ if (flash->isstacked) {
+ /* Wait until finished previous write command. */
+ if (wait_till_ready(flash))
+ return 1;
+
+ flash->spi->master->flags |= SPI_MASTER_U_PAGE;
+
+ /* Send write enable, then erase commands. */
+ write_enable(flash);
+
+ /* Set up command buffer. */
+ flash->command[0] = OPCODE_CHIP_ERASE;
+
+ spi_write(flash->spi, flash->command, 1);
+ }
+
return 0;
}
static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd)
{
+ int i;
+
/* opcode is in cmd[0] */
- cmd[1] = addr >> (flash->addr_width * 8 - 8);
- cmd[2] = addr >> (flash->addr_width * 8 - 16);
- cmd[3] = addr >> (flash->addr_width * 8 - 24);
- cmd[4] = addr >> (flash->addr_width * 8 - 32);
+ for (i = 1; i <= flash->addr_width; i++)
+ cmd[i] = addr >> (flash->addr_width * 8 - i * 8);
}
static int m25p_cmdsz(struct m25p *flash)
@@ -250,6 +367,10 @@ static int erase_sector(struct m25p *fla
if (wait_till_ready(flash))
return 1;
+ /* update Extended Address Register */
+ if (write_ear(flash, offset))
+ return 1;
+
/* Send write enable, then erase commands. */
write_enable(flash);
@@ -275,7 +396,7 @@ static int erase_sector(struct m25p *fla
static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct m25p *flash = mtd_to_m25p(mtd);
- u32 addr,len;
+ u32 addr, len, offset;
uint32_t rem;
pr_debug("%s: %s at 0x%llx, len %lld\n", dev_name(&flash->spi->dev),
@@ -307,7 +428,19 @@ static int m25p80_erase(struct mtd_info
/* "sector"-at-a-time erase */
} else {
while (len) {
- if (erase_sector(flash, addr)) {
+ offset = addr;
+ if (flash->isparallel == 1)
+ offset /= 2;
+ if (flash->isstacked == 1) {
+ if (offset >= (flash->mtd.size / 2)) {
+ offset = offset - (flash->mtd.size / 2);
+ flash->spi->master->flags |=
+ SPI_MASTER_U_PAGE;
+ } else
+ flash->spi->master->flags &=
+ ~SPI_MASTER_U_PAGE;
+ }
+ if (erase_sector(flash, offset)) {
instr->state = MTD_ERASE_FAILED;
mutex_unlock(&flash->lock);
return -EIO;
@@ -336,7 +469,6 @@ static int m25p80_read(struct mtd_info *
struct m25p *flash = mtd_to_m25p(mtd);
struct spi_transfer t[2];
struct spi_message m;
- uint8_t opcode;
pr_debug("%s: %s from 0x%08x, len %zd\n", dev_name(&flash->spi->dev),
__func__, (u32)from, len);
@@ -349,21 +481,17 @@ static int m25p80_read(struct mtd_info *
* Should add 1 byte DUMMY_BYTE.
*/
t[0].tx_buf = flash->command;
- t[0].len = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0);
+ t[0].len = m25p_cmdsz(flash) + flash->dummycount;
spi_message_add_tail(&t[0], &m);
t[1].rx_buf = buf;
t[1].len = len;
spi_message_add_tail(&t[1], &m);
- mutex_lock(&flash->lock);
-
/* Wait till previous write/erase is done. */
- if (wait_till_ready(flash)) {
+ if (wait_till_ready(flash))
/* REVISIT status return?? */
- mutex_unlock(&flash->lock);
return 1;
- }
/* FIXME switch to OPCODE_FAST_READ. It's required for higher
* clocks; and at this writing, every chip this driver handles
@@ -371,17 +499,65 @@ static int m25p80_read(struct mtd_info *
*/
/* Set up the write data buffer. */
- opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ;
- flash->command[0] = opcode;
+ flash->command[0] = flash->read_opcode;
m25p_addr2cmd(flash, from, flash->command);
spi_sync(flash->spi, &m);
*retlen = m.actual_length - m25p_cmdsz(flash) -
- (flash->fast_read ? 1 : 0);
+ flash->dummycount;
- mutex_unlock(&flash->lock);
+ return 0;
+}
+
+static int m25p80_read_ext(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct m25p *flash = mtd_to_m25p(mtd);
+ u32 addr = from;
+ u32 offset = from;
+ u32 read_len = 0;
+ u32 actual_len = 0;
+ u32 read_count = 0;
+ u32 rem_bank_len = 0;
+ u8 bank = 0;
+
+#define OFFSET_16_MB 0x1000000
+
+ mutex_lock(&flash->lock);
+
+ while (len) {
+ bank = addr / (OFFSET_16_MB << flash->shift);
+ rem_bank_len = ((OFFSET_16_MB << flash->shift) * (bank + 1)) -
+ addr;
+ offset = addr;
+ if (flash->isparallel == 1)
+ offset /= 2;
+ if (flash->isstacked == 1) {
+ if (offset >= (flash->mtd.size / 2)) {
+ offset = offset - (flash->mtd.size / 2);
+ flash->spi->master->flags |= SPI_MASTER_U_PAGE;
+ } else {
+ flash->spi->master->flags &= ~SPI_MASTER_U_PAGE;
+ }
+ }
+ write_ear(flash, offset);
+ if (len < rem_bank_len)
+ read_len = len;
+ else
+ read_len = rem_bank_len;
+
+ m25p80_read(mtd, offset, read_len, &actual_len, buf);
+ addr += actual_len;
+ len -= actual_len;
+ buf += actual_len;
+ read_count += actual_len;
+ }
+
+ *retlen = read_count;
+
+ mutex_unlock(&flash->lock);
return 0;
}
@@ -411,19 +587,15 @@ static int m25p80_write(struct mtd_info
t[1].tx_buf = buf;
spi_message_add_tail(&t[1], &m);
- mutex_lock(&flash->lock);
-
/* Wait until finished previous write command. */
- if (wait_till_ready(flash)) {
- mutex_unlock(&flash->lock);
+ if (wait_till_ready(flash))
return 1;
- }
write_enable(flash);
/* Set up the opcode in the write buffer. */
- flash->command[0] = OPCODE_PP;
- m25p_addr2cmd(flash, to, flash->command);
+ flash->command[0] = flash->prog_opcode;
+ m25p_addr2cmd(flash, (to >> flash->shift), flash->command);
page_offset = to & (flash->page_size - 1);
@@ -452,12 +624,14 @@ static int m25p80_write(struct mtd_info
page_size = flash->page_size;
/* write the next page to flash */
- m25p_addr2cmd(flash, to + i, flash->command);
+ m25p_addr2cmd(flash, ((to + i) >> flash->shift),
+ flash->command);
t[1].tx_buf = buf + i;
t[1].len = page_size;
- wait_till_ready(flash);
+ if (wait_till_ready(flash))
+ return 1;
write_enable(flash);
@@ -467,8 +641,55 @@ static int m25p80_write(struct mtd_info
}
}
- mutex_unlock(&flash->lock);
+ return 0;
+}
+
+static int m25p80_write_ext(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct m25p *flash = mtd_to_m25p(mtd);
+ u32 addr = to;
+ u32 offset = to;
+ u32 write_len = 0;
+ u32 actual_len = 0;
+ u32 write_count = 0;
+ u32 rem_bank_len = 0;
+ u8 bank = 0;
+
+#define OFFSET_16_MB 0x1000000
+
+ mutex_lock(&flash->lock);
+ while (len) {
+ bank = addr / (OFFSET_16_MB << flash->shift);
+ rem_bank_len = ((OFFSET_16_MB << flash->shift) * (bank + 1)) -
+ addr;
+ offset = addr;
+
+ if (flash->isstacked == 1) {
+ if (offset >= (flash->mtd.size / 2)) {
+ offset = offset - (flash->mtd.size / 2);
+ flash->spi->master->flags |= SPI_MASTER_U_PAGE;
+ } else {
+ flash->spi->master->flags &= ~SPI_MASTER_U_PAGE;
+ }
+ }
+ write_ear(flash, (offset >> flash->shift));
+ if (len < rem_bank_len)
+ write_len = len;
+ else
+ write_len = rem_bank_len;
+ m25p80_write(mtd, offset, write_len, &actual_len, buf);
+
+ addr += actual_len;
+ len -= actual_len;
+ buf += actual_len;
+ write_count += actual_len;
+ }
+
+ *retlen = write_count;
+
+ mutex_unlock(&flash->lock);
return 0;
}
@@ -682,6 +903,8 @@ struct flash_info {
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
#define M25P_NO_ERASE 0x02 /* No erase command needed */
#define SST_WRITE 0x04 /* use SST byte programming */
+#define SECT_32K 0x10 /* OPCODE_BE_32K */
+#define E_FSR 0x08 /* Flag SR exists for flash */
};
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
@@ -761,6 +984,15 @@ static const struct spi_device_id m25p_i
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) },
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) },
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) },
+ /* Numonyx flash n25q128 - FIXME check the name */
+ { "n25q128", INFO(0x20bb18, 0, 64 * 1024, 256, 0) },
+ { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, E_FSR) },
+ { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, E_FSR) },
+ { "n25q256a13", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | E_FSR) },
+ { "n25q256a11", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | E_FSR) },
+ { "n25q512a13", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | E_FSR) },
+ { "n25q512a11", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | E_FSR) },
+ { "n25q00aa13", INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | E_FSR) },
/* Spansion -- single (large) sector size only, at least
* for the chips listed here (without boot sectors).
@@ -781,7 +1013,11 @@ static const struct spi_device_id m25p_i
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
- { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
+ /* s25fl064k supports 4KiB, 32KiB and 64KiB sectors erase size. */
+ /* To support JFFS2, the minimum erase size is 8KiB(>4KiB). */
+ /* And thus, the sector size of s25fl064k is set to 32KiB for */
+ /* JFFS2 support. */
+ { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_32K) },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
@@ -789,10 +1025,11 @@ static const struct spi_device_id m25p_i
{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },
{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) },
{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },
- { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) },
- { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
- { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
- { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
+ { "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) },
+ { "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
+ { "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
+ { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
+ { "sst25wf080", INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
/* ST Microelectronics -- newer production may have feature updates */
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
@@ -839,7 +1076,12 @@ static const struct spi_device_id m25p_i
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
- { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
+ /* Winbond -- w25q "blocks" are 64K, "sectors" are 32KiB */
+ /* w25q64 supports 4KiB, 32KiB and 64KiB sectors erase size. */
+ /* To support JFFS2, the minimum erase size is 8KiB(>4KiB). */
+ /* And thus, the sector size of w25q64 is set to 32KiB for */
+ /* JFFS2 support. */
+ { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_32K) },
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
@@ -995,8 +1237,54 @@ static int m25p_probe(struct spi_device
flash->mtd.writesize = 1;
flash->mtd.flags = MTD_CAP_NORFLASH;
flash->mtd.size = info->sector_size * info->n_sectors;
+
+ {
+#ifdef CONFIG_OF
+ const char *comp_str;
+ u32 is_dual;
+ np = of_get_next_parent(spi->dev.of_node);
+ of_property_read_string(np, "compatible", &comp_str);
+ if (!strcmp(comp_str, "xlnx,ps7-qspi-1.00.a")) {
+ if (of_property_read_u32(np, "is-dual", &is_dual) < 0) {
+ /* Default to single if prop not defined */
+ flash->shift = 0;
+ flash->isstacked = 0;
+ flash->isparallel = 0;
+ } else {
+ if (is_dual == 1) {
+ /* dual parallel */
+ flash->shift = 1;
+ info->sector_size <<= flash->shift;
+ info->page_size <<= flash->shift;
+ flash->mtd.size <<= flash->shift;
+ flash->isparallel = 1;
+ flash->isstacked = 0;
+ } else {
+#ifdef CONFIG_SPI_XILINX_PS_QSPI_DUAL_STACKED
+ /* dual stacked */
+ flash->shift = 0;
+ flash->mtd.size <<= 1;
+ flash->isstacked = 1;
+ flash->isparallel = 0;
+#else
+ /* single */
+ flash->shift = 0;
+ flash->isstacked = 0;
+ flash->isparallel = 0;
+#endif
+ }
+ }
+ }
+#else
+ /* Default to single */
+ flash->shift = 0;
+ flash->isstacked = 0;
+ flash->isparallel = 0;
+#endif
+ }
+
flash->mtd._erase = m25p80_erase;
- flash->mtd._read = m25p80_read;
+ flash->mtd._read = m25p80_read_ext;
/* flash protection support for STmicro chips */
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
@@ -1008,20 +1296,31 @@ static int m25p_probe(struct spi_device
if (info->flags & SST_WRITE)
flash->mtd._write = sst_write;
else
- flash->mtd._write = m25p80_write;
+ flash->mtd._write = m25p80_write_ext;
/* prefer "small sector" erase if possible */
if (info->flags & SECT_4K) {
flash->erase_opcode = OPCODE_BE_4K;
- flash->mtd.erasesize = 4096;
+ flash->mtd.erasesize = 4096 << flash->shift;
+ } else if (info->flags & SECT_32K) {
+ flash->erase_opcode = OPCODE_BE_32K;
+ flash->mtd.erasesize = 32768 << flash->shift;
} else {
flash->erase_opcode = OPCODE_SE;
flash->mtd.erasesize = info->sector_size;
}
+ flash->read_opcode = OPCODE_NORM_READ;
+ flash->prog_opcode = OPCODE_PP;
+ flash->dummycount = 0;
+
if (info->flags & M25P_NO_ERASE)
flash->mtd.flags |= MTD_NO_ERASE;
+ if (info->flags & E_FSR)
+ flash->check_fsr = 1;
+
+ flash->jedec_id = info->jedec_id;
ppdata.of_node = spi->dev.of_node;
flash->mtd.dev.parent = &spi->dev;
flash->page_size = info->page_size;
@@ -1036,14 +1335,36 @@ static int m25p_probe(struct spi_device
#ifdef CONFIG_M25PXX_USE_FAST_READ
flash->fast_read = true;
#endif
+ if (flash->fast_read) {
+ flash->read_opcode = OPCODE_FAST_READ;
+ flash->dummycount = 1;
+ }
+
+ if (spi->master->flags & SPI_MASTER_QUAD_MODE) {
+ flash->read_opcode = OPCODE_QUAD_READ;
+ flash->prog_opcode = OPCODE_QPP;
+ flash->dummycount = 1;
+ }
if (info->addr_width)
flash->addr_width = info->addr_width;
else {
/* enable 4-byte addressing if the device exceeds 16MiB */
if (flash->mtd.size > 0x1000000) {
- flash->addr_width = 4;
- set_4byte(flash, info->jedec_id, 1);
+#ifdef CONFIG_OF
+ const char *comp_str;
+ np = of_get_next_parent(spi->dev.of_node);
+ of_property_read_string(np, "compatible", &comp_str);
+ if (!strcmp(comp_str, "xlnx,ps7-qspi-1.00.a")) {
+ flash->addr_width = 3;
+ set_4byte(flash, info->jedec_id, 0);
+ } else {
+#endif
+ flash->addr_width = 4;
+ set_4byte(flash, info->jedec_id, 1);
+#ifdef CONFIG_OF
+ }
+#endif
} else
flash->addr_width = 3;
}
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -523,6 +523,13 @@ config MTD_NAND_NUC900
This enables the driver for the NAND Flash on evaluation board based
on w90p910 / NUC9xx.
+config MTD_NAND_XILINX_PS
+ tristate "Xilinx Zynq NAND flash driver"
+ depends on MTD_NAND && ARCH_ZYNQ
+ select ZYNQ_SMC
+ help
+ This enables access to the NAND flash device on Xilinx Zynq.
+
config MTD_NAND_JZ4740
tristate "Support for JZ4740 SoC NAND controller"
depends on MACH_JZ4740
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
+obj-$(CONFIG_MTD_NAND_XILINX_PS) += xilinx_nandps.o
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -814,6 +814,11 @@ static int nand_wait(struct mtd_info *mt
int status, state = chip->state;
unsigned long timeo = (state == FL_ERASING ? 400 : 20);
+#if defined(ARCH_ZYNQ) && (CONFIG_HZ == 20)
+ /* Xilinx Zynq NAND work around for HZ=20 */
+ timeo += 1;
+#endif
+
led_trigger_event(nand_led_trigger, LED_FULL);
/*
@@ -2858,11 +2863,18 @@ static int nand_flash_detect_onfi(struct
int i;
int val;
+#ifdef CONFIG_MTD_NAND_XILINX_PS
+ uint8_t *buf;
+ unsigned int options;
+ int j;
+#endif
+
/* ONFI need to be probed in 8 bits mode, and 16 bits should be selected with NAND_BUSWIDTH_AUTO */
if (chip->options & NAND_BUSWIDTH_16) {
pr_err("Trying ONFI probe in 16 bits mode, aborting !\n");
return 0;
}
+
/* Try ONFI for unknown chip or LP */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
@@ -2871,7 +2883,13 @@ static int nand_flash_detect_onfi(struct
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
+#ifdef CONFIG_MTD_NAND_XILINX_PS
+ buf = (uint8_t *)p;
+ for (j = 0; j < 256; j++)
+ buf[j] = chip->read_byte(mtd);
+#else
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+#endif
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
pr_info("ONFI param page %d valid\n", i);
@@ -2924,7 +2942,17 @@ static int nand_flash_detect_onfi(struct
if (le16_to_cpu(p->features) & 1)
*busw = NAND_BUSWIDTH_16;
+#ifdef CONFIG_MTD_NAND_XILINX_PS
+ /* Read the chip options before clearing the bits */
+ options = chip->options;
+#endif
+
pr_info("ONFI flash detected\n");
+#ifdef CONFIG_MTD_NAND_XILINX_PS
+ /* set the bus width option */
+ if (options & NAND_BUSWIDTH_16)
+ chip->options |= NAND_BUSWIDTH_16;
+#endif
return 1;
}
--- /dev/null
+++ b/drivers/mtd/nand/xilinx_nandps.c
@@ -0,0 +1,1064 @@
+/*
+ * Xilinx Zynq NAND Flash Controller Driver
+ *
+ * Copyright (C) 2009 Xilinx, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This driver is based on plat_nand.c and mxc_nand.c drivers
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/memory/zynq-smc.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define XNANDPS_DRIVER_NAME "xilinx_nandps"
+
+/* NAND flash driver defines */
+#define XNANDPS_CMD_PHASE 1 /* End command valid in command phase */
+#define XNANDPS_DATA_PHASE 2 /* End command valid in data phase */
+#define XNANDPS_ECC_SIZE 512 /* Size of data for ECC operation */
+
+/* Flash memory controller operating parameters */
+
+#define XNANDPS_ECC_CONFIG ((1 << 4) | /* ECC read at end of page */ \
+ (0 << 5)) /* No Jumping */
+
+/* AXI Address definitions */
+#define START_CMD_SHIFT 3
+#define END_CMD_SHIFT 11
+#define END_CMD_VALID_SHIFT 20
+#define ADDR_CYCLES_SHIFT 21
+#define CLEAR_CS_SHIFT 21
+#define ECC_LAST_SHIFT 10
+#define COMMAND_PHASE (0 << 19)
+#define DATA_PHASE (1 << 19)
+
+#define XNANDPS_ECC_LAST (1 << ECC_LAST_SHIFT) /* Set ECC_Last */
+#define XNANDPS_CLEAR_CS (1 << CLEAR_CS_SHIFT) /* Clear chip select */
+
+#define ONDIE_ECC_FEATURE_ADDR 0x90
+
+/* Macros for the NAND controller register read/write */
+#define xnandps_write32(addr, val) __raw_writel((val), (addr))
+
+
+/**
+ * struct xnandps_command_format - Defines NAND flash command format
+ * @start_cmd: First cycle command (Start command)
+ * @end_cmd: Second cycle command (Last command)
+ * @addr_cycles: Number of address cycles required to send the address
+ * @end_cmd_valid: The second cycle command is valid for cmd or data phase
+ **/
+struct xnandps_command_format {
+ int start_cmd;
+ int end_cmd;
+ u8 addr_cycles;
+ u8 end_cmd_valid;
+};
+
+/**
+ * struct xnandps_info - Defines the NAND flash driver instance
+ * @chip: NAND chip information structure
+ * @mtd: MTD information structure
+ * @parts: Pointer to the mtd_partition structure
+ * @nand_base: Virtual address of the NAND flash device
+ * @end_cmd_pending: End command is pending
+ * @end_cmd: End command
+ **/
+struct xnandps_info {
+ struct nand_chip chip;
+ struct mtd_info mtd;
+ struct mtd_partition *parts;
+ void __iomem *nand_base;
+ unsigned long end_cmd_pending;
+ unsigned long end_cmd;
+};
+
+/*
+ * The NAND flash operations command format
+ */
+static const struct xnandps_command_format xnandps_commands[] = {
+ {NAND_CMD_READ0, NAND_CMD_READSTART, 5, XNANDPS_CMD_PHASE},
+ {NAND_CMD_RNDOUT, NAND_CMD_RNDOUTSTART, 2, XNANDPS_CMD_PHASE},
+ {NAND_CMD_READID, NAND_CMD_NONE, 1, NAND_CMD_NONE},
+ {NAND_CMD_STATUS, NAND_CMD_NONE, 0, NAND_CMD_NONE},
+ {NAND_CMD_SEQIN, NAND_CMD_PAGEPROG, 5, XNANDPS_DATA_PHASE},
+ {NAND_CMD_RNDIN, NAND_CMD_NONE, 2, NAND_CMD_NONE},
+ {NAND_CMD_ERASE1, NAND_CMD_ERASE2, 3, XNANDPS_CMD_PHASE},
+ {NAND_CMD_RESET, NAND_CMD_NONE, 0, NAND_CMD_NONE},
+ {NAND_CMD_PARAM, NAND_CMD_NONE, 1, NAND_CMD_NONE},
+ {NAND_CMD_GET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
+ {NAND_CMD_SET_FEATURES, NAND_CMD_NONE, 1, NAND_CMD_NONE},
+ {NAND_CMD_NONE, NAND_CMD_NONE, 0, 0},
+ /* Add all the flash commands supported by the flash device and Linux */
+ /* The cache program command is not supported by driver because driver
+ * cant differentiate between page program and cached page program from
+ * start command, these commands can be differentiated through end
+ * command, which doesn't fit in to the driver design. The cache program
+ * command is not supported by NAND subsystem also, look at 1612 line
+ * number (in nand_write_page function) of nand_base.c file.
+ * {NAND_CMD_SEQIN, NAND_CMD_CACHEDPROG, 5, XNANDPS_YES}, */
+};
+
+/* Define default oob placement schemes for large and small page devices */
+static struct nand_ecclayout nand_oob_16 = {
+ .eccbytes = 3,
+ .eccpos = {0, 1, 2},
+ .oobfree = {
+ {.offset = 8,
+ . length = 8} }
+};
+
+static struct nand_ecclayout nand_oob_64 = {
+ .eccbytes = 12,
+ .eccpos = {
+ 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63},
+ .oobfree = {
+ {.offset = 2,
+ .length = 50} }
+};
+
+static struct nand_ecclayout ondie_nand_oob_64 = {
+ .eccbytes = 32,
+
+ .eccpos = {
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 56, 57, 58, 59, 60, 61, 62, 63
+ },
+
+ .oobfree = {
+ { .offset = 4, .length = 4 },
+ { .offset = 20, .length = 4 },
+ { .offset = 36, .length = 4 },
+ { .offset = 52, .length = 4 }
+ }
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 4,
+ .len = 4,
+ .veroffs = 20,
+ .maxblocks = 4,
+ .pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+ | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 4,
+ .len = 4,
+ .veroffs = 20,
+ .maxblocks = 4,
+ .pattern = mirror_pattern
+};
+
+/**
+ * xnandps_calculate_hwecc - Calculate Hardware ECC
+ * @mtd: Pointer to the mtd_info structure
+ * @data: Pointer to the page data
+ * @ecc_code: Pointer to the ECC buffer where ECC data needs to be stored
+ *
+ * This function retrieves the Hardware ECC data from the controller and returns
+ * ECC data back to the MTD subsystem.
+ *
+ * returns: 0 on success or error value on failure
+ **/
+static int
+xnandps_calculate_hwecc(struct mtd_info *mtd, const u8 *data, u8 *ecc_code)
+{
+ u32 ecc_value = 0;
+ u8 ecc_reg, ecc_byte;
+ u32 ecc_status;
+
+ /* Wait till the ECC operation is complete */
+ while (xsmcps_ecc_is_busy())
+ cpu_relax();
+
+ for (ecc_reg = 0; ecc_reg < 4; ecc_reg++) {
+ /* Read ECC value for each block */
+ ecc_value = xsmcps_get_ecc_val(ecc_reg);
+ ecc_status = (ecc_value >> 24) & 0xFF;
+ /* ECC value valid */
+ if (ecc_status & 0x40) {
+ for (ecc_byte = 0; ecc_byte < 3; ecc_byte++) {
+ /* Copy ECC bytes to MTD buffer */
+ *ecc_code = ecc_value & 0xFF;
+ ecc_value = ecc_value >> 8;
+ ecc_code++;
+ }
+ } else {
+ /* TO DO */
+ /* dev_warn(&pdev->dev, "pl350: ecc status failed\n");
+ * */
+ }
+ }
+ return 0;
+}
+
+/**
+ * onehot - onehot function
+ * @value: value to check for onehot
+ *
+ * This function checks whether a value is onehot or not.
+ * onehot is if and only if onebit is set.
+ *
+ **/
+static int onehot(unsigned short value)
+{
+ return ((value & (value-1)) == 0);
+}
+
+/**
+ * xnandps_correct_data - ECC correction function
+ * @mtd: Pointer to the mtd_info structure
+ * @buf: Pointer to the page data
+ * @read_ecc: Pointer to the ECC value read from spare data area
+ * @calc_ecc: Pointer to the calculated ECC value
+ *
+ * This function corrects the ECC single bit errors & detects 2-bit errors.
+ *
+ * returns: 0 if no ECC errors found
+ * 1 if single bit error found and corrected.
+ * -1 if multiple ECC errors found.
+ **/
+static int xnandps_correct_data(struct mtd_info *mtd, unsigned char *buf,
+ unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+ unsigned char bit_addr;
+ unsigned int byte_addr;
+ unsigned short ecc_odd, ecc_even;
+ unsigned short read_ecc_lower, read_ecc_upper;
+ unsigned short calc_ecc_lower, calc_ecc_upper;
+
+ read_ecc_lower = (read_ecc[0] | (read_ecc[1] << 8)) & 0xfff;
+ read_ecc_upper = ((read_ecc[1] >> 4) | (read_ecc[2] << 4)) & 0xfff;
+
+ calc_ecc_lower = (calc_ecc[0] | (calc_ecc[1] << 8)) & 0xfff;
+ calc_ecc_upper = ((calc_ecc[1] >> 4) | (calc_ecc[2] << 4)) & 0xfff;
+
+ ecc_odd = read_ecc_lower ^ calc_ecc_lower;
+ ecc_even = read_ecc_upper ^ calc_ecc_upper;
+
+ if ((ecc_odd == 0) && (ecc_even == 0))
+ return 0; /* no error */
+
+ if (ecc_odd == (~ecc_even & 0xfff)) {
+ /* bits [11:3] of error code is byte offset */
+ byte_addr = (ecc_odd >> 3) & 0x1ff;
+ /* bits [2:0] of error code is bit offset */
+ bit_addr = ecc_odd & 0x7;
+ /* Toggling error bit */
+ buf[byte_addr] ^= (1 << bit_addr);
+ return 1;
+ }
+
+ if (onehot(ecc_odd | ecc_even) == 1)
+ return 1; /* one error in parity */
+
+ return -1; /* Uncorrectable error */
+}
+
+/**
+ * xnandps_read_oob - [REPLACABLE] the most common OOB data read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to read
+ */
+static int xnandps_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ unsigned long data_width = 4;
+ unsigned long data_phase_addr = 0;
+ uint8_t *p;
+
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+
+ p = chip->oob_poi;
+ chip->read_buf(mtd, p, (mtd->oobsize - data_width));
+ p += (mtd->oobsize - data_width);
+
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
+ data_phase_addr |= XNANDPS_CLEAR_CS;
+ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr;
+ chip->read_buf(mtd, p, data_width);
+
+ return 0;
+}
+
+/**
+ * xnandps_write_oob - [REPLACABLE] the most common OOB data write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @page: page number to write
+ */
+static int xnandps_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+ int page)
+{
+ int status = 0;
+ const uint8_t *buf = chip->oob_poi;
+ unsigned long data_width = 4;
+ unsigned long data_phase_addr = 0;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+
+ chip->write_buf(mtd, buf, (mtd->oobsize - data_width));
+ buf += (mtd->oobsize - data_width);
+
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
+ data_phase_addr |= XNANDPS_CLEAR_CS;
+ data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
+ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr;
+ chip->write_buf(mtd, buf, data_width);
+
+ /* Send command to program the OOB data */
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+/**
+ * xnandps_read_page_raw - [Intern] read raw page data without ecc
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @page: page number to read
+ *
+ */
+static int xnandps_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ unsigned long data_width = 4;
+ unsigned long data_phase_addr = 0;
+ uint8_t *p;
+
+ chip->read_buf(mtd, buf, mtd->writesize);
+
+ p = chip->oob_poi;
+ chip->read_buf(mtd, p, (mtd->oobsize - data_width));
+ p += (mtd->oobsize - data_width);
+
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
+ data_phase_addr |= XNANDPS_CLEAR_CS;
+ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr;
+
+ chip->read_buf(mtd, p, data_width);
+ return 0;
+}
+
+/**
+ * xnandps_write_page_raw - [Intern] raw page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ *
+ */
+static int xnandps_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+ const uint8_t *buf, int oob_required)
+{
+ unsigned long data_width = 4;
+ unsigned long data_phase_addr = 0;
+ uint8_t *p;
+
+ chip->write_buf(mtd, buf, mtd->writesize);
+
+ p = chip->oob_poi;
+ chip->write_buf(mtd, p, (mtd->oobsize - data_width));
+ p += (mtd->oobsize - data_width);
+
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
+ data_phase_addr |= XNANDPS_CLEAR_CS;
+ data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
+ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr;
+
+ chip->write_buf(mtd, p, data_width);
+
+ return 0;
+}
+
+/**
+ * nand_write_page_hwecc - Hardware ECC based page write function
+ * @mtd: Pointer to the mtd info structure
+ * @chip: Pointer to the NAND chip info structure
+ * @buf: Pointer to the data buffer
+ *
+ * This functions writes data and hardware generated ECC values in to the page.
+ */
+static int xnandps_write_page_hwecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ const uint8_t *p = buf;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ unsigned long data_phase_addr = 0;
+ unsigned long data_width = 4;
+ uint8_t *oob_ptr;
+
+ for (; (eccsteps - 1); eccsteps--) {
+ chip->write_buf(mtd, p, eccsize);
+ p += eccsize;
+ }
+ chip->write_buf(mtd, p, (eccsize - data_width));
+ p += (eccsize - data_width);
+
+ /* Set ECC Last bit to 1 */
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
+ data_phase_addr |= XNANDPS_ECC_LAST;
+ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr;
+ chip->write_buf(mtd, p, data_width);
+
+ /* Wait for ECC to be calculated and read the error values */
+ p = buf;
+ chip->ecc.calculate(mtd, p, &ecc_calc[0]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ~(ecc_calc[i]);
+
+ /* Clear ECC last bit */
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
+ data_phase_addr &= ~XNANDPS_ECC_LAST;
+ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr;
+
+ /* Write the spare area with ECC bytes */
+ oob_ptr = chip->oob_poi;
+ chip->write_buf(mtd, oob_ptr, (mtd->oobsize - data_width));
+
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_W;
+ data_phase_addr |= XNANDPS_CLEAR_CS;
+ data_phase_addr |= (1 << END_CMD_VALID_SHIFT);
+ chip->IO_ADDR_W = (void __iomem *__force)data_phase_addr;
+ oob_ptr += (mtd->oobsize - data_width);
+ chip->write_buf(mtd, oob_ptr, data_width);
+
+ return 0;
+}
+
+/**
+ * xnandps_write_page_swecc - [REPLACABLE] software ecc based page write function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: data buffer
+ */
+static int xnandps_write_page_swecc(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ const uint8_t *p = buf;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ /* Software ecc calculation */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+ chip->ecc.write_page_raw(mtd, chip, buf, 1);
+
+ return 0;
+}
+
+/**
+ * xnandps_read_page_hwecc - Hardware ECC based page read function
+ * @mtd: Pointer to the mtd info structure
+ * @chip: Pointer to the NAND chip info structure
+ * @buf: Pointer to the buffer to store read data
+ * @page: page number to read
+ *
+ * This functions reads data and checks the data integrity by comparing hardware
+ * generated ECC values and read ECC values from spare area.
+ *
+ * returns: 0 always and updates ECC operation status in to MTD structure
+ */
+static int xnandps_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, stat, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ unsigned long data_phase_addr = 0;
+ unsigned long data_width = 4;
+ uint8_t *oob_ptr;
+
+ for (; (eccsteps - 1); eccsteps--) {
+ chip->read_buf(mtd, p, eccsize);
+ p += eccsize;
+ }
+ chip->read_buf(mtd, p, (eccsize - data_width));
+ p += (eccsize - data_width);
+
+ /* Set ECC Last bit to 1 */
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
+ data_phase_addr |= XNANDPS_ECC_LAST;
+ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr;
+ chip->read_buf(mtd, p, data_width);
+
+ /* Read the calculated ECC value */
+ p = buf;
+ chip->ecc.calculate(mtd, p, &ecc_calc[0]);
+
+ /* Clear ECC last bit */
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
+ data_phase_addr &= ~XNANDPS_ECC_LAST;
+ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr;
+
+ /* Read the stored ECC value */
+ oob_ptr = chip->oob_poi;
+ chip->read_buf(mtd, oob_ptr, (mtd->oobsize - data_width));
+
+ /* de-assert chip select */
+ data_phase_addr = (unsigned long __force)chip->IO_ADDR_R;
+ data_phase_addr |= XNANDPS_CLEAR_CS;
+ chip->IO_ADDR_R = (void __iomem *__force)data_phase_addr;
+
+ oob_ptr += (mtd->oobsize - data_width);
+ chip->read_buf(mtd, oob_ptr, data_width);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = ~(chip->oob_poi[eccpos[i]]);
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ /* Check ECC error for all blocks and correct if it is correctable */
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+}
+
+/**
+ * xnandps_read_page_swecc - [REPLACABLE] software ecc based page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @page: page number to read
+ */
+static int xnandps_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+ uint8_t *buf, int oob_required, int page)
+{
+ int i, eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ int eccsteps = chip->ecc.steps;
+ uint8_t *p = buf;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+ chip->ecc.read_page_raw(mtd, chip, buf, page, 1);
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ eccsteps = chip->ecc.steps;
+ p = buf;
+
+ for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+ return 0;
+}
+
+/**
+ * xnandps_select_chip - Select the flash device
+ * @mtd: Pointer to the mtd_info structure
+ * @chip: Chip number to be selected
+ *
+ * This function is empty as the NAND controller handles chip select line
+ * internally based on the chip address passed in command and data phase.
+ **/
+static void xnandps_select_chip(struct mtd_info *mtd, int chip)
+{
+ return;
+}
+
+/**
+ * xnandps_cmd_function - Send command to NAND device
+ * @mtd: Pointer to the mtd_info structure
+ * @command: The command to be sent to the flash device
+ * @column: The column address for this command, -1 if none
+ * @page_addr: The page address for this command, -1 if none
+ */
+static void xnandps_cmd_function(struct mtd_info *mtd, unsigned int command,
+ int column, int page_addr)
+{
+ struct nand_chip *chip = mtd->priv;
+ const struct xnandps_command_format *curr_cmd = NULL;
+ struct xnandps_info *xnand =
+ container_of(mtd, struct xnandps_info, mtd);
+ void __iomem *cmd_addr;
+ unsigned long cmd_data = 0;
+ unsigned long cmd_phase_addr = 0;
+ unsigned long data_phase_addr = 0;
+ unsigned long end_cmd = 0;
+ unsigned long end_cmd_valid = 0;
+ unsigned long i;
+
+ if (xnand->end_cmd_pending) {
+ /* Check for end command if this command request is same as the
+ * pending command then return */
+ if (xnand->end_cmd == command) {
+ xnand->end_cmd = 0;
+ xnand->end_cmd_pending = 0;
+ return;
+ }
+ }
+
+ /* Emulate NAND_CMD_READOOB for large page device */
+ if ((mtd->writesize > XNANDPS_ECC_SIZE) &&
+ (command == NAND_CMD_READOOB)) {
+ column += mtd->writesize;
+ command = NAND_CMD_READ0;
+ }
+
+ /* Get the command format */
+ for (i = 0; (xnandps_commands[i].start_cmd != NAND_CMD_NONE ||
+ xnandps_commands[i].end_cmd != NAND_CMD_NONE); i++) {
+ if (command == xnandps_commands[i].start_cmd)
+ curr_cmd = &xnandps_commands[i];
+ }
+
+ if (curr_cmd == NULL)
+ return;
+
+ /* Clear interrupt */
+ xsmcps_clr_nand_int();
+
+ /* Get the command phase address */
+ if (curr_cmd->end_cmd_valid == XNANDPS_CMD_PHASE)
+ end_cmd_valid = 1;
+
+ if (curr_cmd->end_cmd == NAND_CMD_NONE)
+ end_cmd = 0x0;
+ else
+ end_cmd = curr_cmd->end_cmd;
+
+ cmd_phase_addr = (unsigned long __force)xnand->nand_base |
+ (curr_cmd->addr_cycles << ADDR_CYCLES_SHIFT) |
+ (end_cmd_valid << END_CMD_VALID_SHIFT) |
+ (COMMAND_PHASE) |
+ (end_cmd << END_CMD_SHIFT) |
+ (curr_cmd->start_cmd << START_CMD_SHIFT);
+
+ cmd_addr = (void __iomem * __force)cmd_phase_addr;
+
+ /* Get the data phase address */
+ end_cmd_valid = 0;
+
+ data_phase_addr = (unsigned long __force)xnand->nand_base |
+ (0x0 << CLEAR_CS_SHIFT) |
+ (end_cmd_valid << END_CMD_VALID_SHIFT) |
+ (DATA_PHASE) |
+ (end_cmd << END_CMD_SHIFT) |
+ (0x0 << ECC_LAST_SHIFT);
+
+ chip->IO_ADDR_R = (void __iomem * __force)data_phase_addr;
+ chip->IO_ADDR_W = chip->IO_ADDR_R;
+
+ /* Command phase AXI write */
+ /* Read & Write */
+ if (column != -1 && page_addr != -1) {
+ /* Adjust columns for 16 bit bus width */
+ if (chip->options & NAND_BUSWIDTH_16)
+ column >>= 1;
+ cmd_data = column;
+ if (mtd->writesize > XNANDPS_ECC_SIZE) {
+ cmd_data |= page_addr << 16;
+ /* Another address cycle for devices > 128MiB */
+ if (chip->chipsize > (128 << 20)) {
+ xnandps_write32(cmd_addr, cmd_data);
+ cmd_data = (page_addr >> 16);
+ }
+ } else {
+ cmd_data |= page_addr << 8;
+ }
+ } else if (page_addr != -1) {
+ /* Erase */
+ cmd_data = page_addr;
+ } else if (column != -1) {
+ /* Change read/write column, read id etc */
+ /* Adjust columns for 16 bit bus width */
+ if ((chip->options & NAND_BUSWIDTH_16) &&
+ ((command == NAND_CMD_READ0) ||
+ (command == NAND_CMD_SEQIN) ||
+ (command == NAND_CMD_RNDOUT) ||
+ (command == NAND_CMD_RNDIN)))
+ column >>= 1;
+ cmd_data = column;
+ }
+
+ xnandps_write32(cmd_addr, cmd_data);
+
+ if (curr_cmd->end_cmd_valid) {
+ xnand->end_cmd = curr_cmd->end_cmd;
+ xnand->end_cmd_pending = 1;
+ }
+
+ ndelay(100);
+
+ if ((command == NAND_CMD_READ0) ||
+ (command == NAND_CMD_RESET) ||
+ (command == NAND_CMD_PARAM) ||
+ (command == NAND_CMD_GET_FEATURES)) {
+
+ while (!chip->dev_ready(mtd))
+ ;
+ return;
+ }
+}
+
+/**
+ * xnandps_read_buf - read chip data into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ *
+ */
+static void xnandps_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+ unsigned long *ptr = (unsigned long *)buf;
+
+ len >>= 2;
+ for (i = 0; i < len; i++)
+ ptr[i] = readl(chip->IO_ADDR_R);
+}
+
+/**
+ * xnandps_write_buf - write buffer to chip
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ *
+ */
+static void xnandps_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+ unsigned long *ptr = (unsigned long *)buf;
+ len >>= 2;
+
+ for (i = 0; i < len; i++)
+ writel(ptr[i], chip->IO_ADDR_W);
+}
+
+/**
+ * xnandps_device_ready - Check device ready/busy line
+ * @mtd: Pointer to the mtd_info structure
+ *
+ * returns: 0 on busy or 1 on ready state
+ **/
+static int xnandps_device_ready(struct mtd_info *mtd)
+{
+ if (xsmcps_get_nand_int_status_raw()) {
+ xsmcps_clr_nand_int();
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * xnandps_probe - Probe method for the NAND driver
+ * @pdev: Pointer to the platform_device structure
+ *
+ * This function initializes the driver data structures and the hardware.
+ *
+ * returns: 0 on success or error value on failure
+ **/
+static int xnandps_probe(struct platform_device *pdev)
+{
+ struct xnandps_info *xnand;
+ struct mtd_info *mtd;
+ struct nand_chip *nand_chip;
+ struct resource *nand_res;
+ u8 maf_id, dev_id, i;
+ u8 get_feature;
+ u8 set_feature[4] = {0x08, 0x00, 0x00, 0x00};
+ int ondie_ecc_enabled = 0;
+ struct mtd_part_parser_data ppdata;
+ const unsigned int *prop;
+ u32 options = 0;
+
+ xnand = devm_kzalloc(&pdev->dev, sizeof(struct xnandps_info),
+ GFP_KERNEL);
+ if (!xnand)
+ return -ENOMEM;
+
+ /* Map physical address of NAND flash */
+ nand_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xnand->nand_base = devm_ioremap_resource(&pdev->dev, nand_res);
+ if (IS_ERR(xnand->nand_base)) {
+ dev_err(&pdev->dev, "ioremap for NAND failed\n");
+ return PTR_ERR(xnand->nand_base);
+ }
+
+ /* Get x8 or x16 mode from device tree */
+ prop = of_get_property(pdev->dev.of_node, "xlnx,nand-width", NULL);
+ if (prop) {
+ if (be32_to_cpup(prop) == 16) {
+ options |= NAND_BUSWIDTH_16;
+ } else if (be32_to_cpup(prop) == 8) {
+ options &= ~NAND_BUSWIDTH_16;
+ } else {
+ dev_info(&pdev->dev, "xlnx,nand-width not valid, using 8");
+ options &= ~NAND_BUSWIDTH_16;
+ }
+ } else {
+ dev_info(&pdev->dev, "xlnx,nand-width not in device tree, using 8");
+ options &= ~NAND_BUSWIDTH_16;
+ }
+
+ /* Link the private data with the MTD structure */
+ mtd = &xnand->mtd;
+ nand_chip = &xnand->chip;
+
+ nand_chip->priv = xnand;
+ mtd->priv = nand_chip;
+ mtd->owner = THIS_MODULE;
+ mtd->name = "xilinx_nand";
+
+ /* Set address of NAND IO lines */
+ nand_chip->IO_ADDR_R = xnand->nand_base;
+ nand_chip->IO_ADDR_W = xnand->nand_base;
+
+ /* Set the driver entry points for MTD */
+ nand_chip->cmdfunc = xnandps_cmd_function;
+ nand_chip->dev_ready = xnandps_device_ready;
+ nand_chip->select_chip = xnandps_select_chip;
+
+ /* If we don't set this delay driver sets 20us by default */
+ nand_chip->chip_delay = 30;
+
+ /* Buffer read/write routines */
+ nand_chip->read_buf = xnandps_read_buf;
+ nand_chip->write_buf = xnandps_write_buf;
+
+ /* Set the device option and flash width */
+ nand_chip->options = options;
+ nand_chip->bbt_options = NAND_BBT_USE_FLASH;
+
+ platform_set_drvdata(pdev, xnand);
+
+ /* first scan to find the device and get the page size */
+ if (nand_scan_ident(mtd, 1, NULL)) {
+ dev_err(&pdev->dev, "nand_scan_ident for NAND failed\n");
+ return -ENXIO;
+ }
+
+ /* Check if On-Die ECC flash */
+ nand_chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ nand_chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+ /* Read manufacturer and device IDs */
+ maf_id = nand_chip->read_byte(mtd);
+ dev_id = nand_chip->read_byte(mtd);
+
+ if ((maf_id == 0x2c) &&
+ ((dev_id == 0xf1) || (dev_id == 0xa1) ||
+ (dev_id == 0xb1) ||
+ (dev_id == 0xaa) || (dev_id == 0xba) ||
+ (dev_id == 0xda) || (dev_id == 0xca) ||
+ (dev_id == 0xac) || (dev_id == 0xbc) ||
+ (dev_id == 0xdc) || (dev_id == 0xcc) ||
+ (dev_id == 0xa3) || (dev_id == 0xb3) ||
+ (dev_id == 0xd3) || (dev_id == 0xc3))) {
+
+ nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
+ ONDIE_ECC_FEATURE_ADDR, -1);
+ get_feature = nand_chip->read_byte(mtd);
+
+ if (get_feature & 0x08) {
+ ondie_ecc_enabled = 1;
+ } else {
+ nand_chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES,
+ ONDIE_ECC_FEATURE_ADDR, -1);
+ for (i = 0; i < 4; i++)
+ writeb(set_feature[i], nand_chip->IO_ADDR_W);
+
+ ndelay(1000);
+
+ nand_chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES,
+ ONDIE_ECC_FEATURE_ADDR, -1);
+ get_feature = nand_chip->read_byte(mtd);
+
+ if (get_feature & 0x08)
+ ondie_ecc_enabled = 1;
+ }
+ }
+
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.read_oob = xnandps_read_oob;
+ nand_chip->ecc.read_page_raw = xnandps_read_page_raw;
+ nand_chip->ecc.strength = 1;
+ nand_chip->ecc.write_oob = xnandps_write_oob;
+ nand_chip->ecc.write_page_raw = xnandps_write_page_raw;
+ if (ondie_ecc_enabled) {
+ /* bypass the controller ECC block */
+ xsmcps_set_ecc_mode(XSMCPS_ECCMODE_BYPASS);
+
+ /* The software ECC routines won't work with the
+ SMC controller */
+ nand_chip->ecc.bytes = 0;
+ nand_chip->ecc.layout = &ondie_nand_oob_64;
+ nand_chip->ecc.read_page = xnandps_read_page_raw;
+ nand_chip->ecc.write_page = xnandps_write_page_raw;
+ nand_chip->ecc.size = mtd->writesize;
+ /* On-Die ECC spare bytes offset 8 is used for ECC codes */
+ /* Use the BBT pattern descriptors */
+ nand_chip->bbt_td = &bbt_main_descr;
+ nand_chip->bbt_md = &bbt_mirror_descr;
+ } else {
+ /* Hardware ECC generates 3 bytes ECC code for each 512 bytes */
+ nand_chip->ecc.bytes = 3;
+ nand_chip->ecc.calculate = xnandps_calculate_hwecc;
+ nand_chip->ecc.correct = xnandps_correct_data;
+ nand_chip->ecc.hwctl = NULL;
+ nand_chip->ecc.read_page = xnandps_read_page_hwecc;
+ nand_chip->ecc.size = XNANDPS_ECC_SIZE;
+ nand_chip->ecc.write_page = xnandps_write_page_hwecc;
+
+ xsmcps_set_ecc_pg_size(mtd->writesize);
+ switch (mtd->writesize) {
+ case 512:
+ case 1024:
+ case 2048:
+ xsmcps_set_ecc_mode(XSMCPS_ECCMODE_APB);
+ break;
+ default:
+ /* The software ECC routines won't work with the
+ SMC controller */
+ nand_chip->ecc.calculate = nand_calculate_ecc;
+ nand_chip->ecc.correct = nand_correct_data;
+ nand_chip->ecc.read_page = xnandps_read_page_swecc;
+ /* nand_chip->ecc.read_subpage = nand_read_subpage; */
+ nand_chip->ecc.write_page = xnandps_write_page_swecc;
+ nand_chip->ecc.size = 256;
+ break;
+ }
+
+ if (mtd->oobsize == 16)
+ nand_chip->ecc.layout = &nand_oob_16;
+ else if (mtd->oobsize == 64)
+ nand_chip->ecc.layout = &nand_oob_64;
+ }
+
+ /* second phase scan */
+ if (nand_scan_tail(mtd)) {
+ dev_err(&pdev->dev, "nand_scan_tail for NAND failed\n");
+ return -ENXIO;
+ }
+
+ ppdata.of_node = pdev->dev.of_node;
+
+ mtd_device_parse_register(&xnand->mtd, NULL, &ppdata,
+ NULL, 0);
+
+ return 0;
+}
+
+/**
+ * xnandps_remove - Remove method for the NAND driver
+ * @pdev: Pointer to the platform_device structure
+ *
+ * This function is called if the driver module is being unloaded. It frees all
+ * resources allocated to the device.
+ *
+ * returns: 0 on success or error value on failure
+ **/
+static int xnandps_remove(struct platform_device *pdev)
+{
+ struct xnandps_info *xnand = platform_get_drvdata(pdev);
+
+ /* Release resources, unregister device */
+ nand_release(&xnand->mtd);
+ /* kfree(NULL) is safe */
+ kfree(xnand->parts);
+
+ return 0;
+}
+
+/* Match table for device tree binding */
+static const struct of_device_id xnandps_of_match[] = {
+ { .compatible = "xlnx,ps7-nand-1.00.a" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, xnandps_of_match);
+
+/*
+ * xnandps_driver - This structure defines the NAND subsystem platform driver
+ */
+static struct platform_driver xnandps_driver = {
+ .probe = xnandps_probe,
+ .remove = xnandps_remove,
+ .driver = {
+ .name = XNANDPS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = xnandps_of_match,
+ },
+};
+
+module_platform_driver(xnandps_driver);
+
+MODULE_AUTHOR("Xilinx, Inc.");
+MODULE_ALIAS("platform:" XNANDPS_DRIVER_NAME);
+MODULE_DESCRIPTION("Xilinx PS NAND Flash Driver");
+MODULE_LICENSE("GPL");