From patchwork Wed Jan 8 23:07:17 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bill Pringlemeir X-Patchwork-Id: 3509241 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 5A599C02DC for ; Sun, 19 Jan 2014 11:43:55 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BF2952011E for ; Sun, 19 Jan 2014 11:43:53 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 6D47A2011B for ; Sun, 19 Jan 2014 11:43:51 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1W4qlC-0006Rx-R9; Sun, 19 Jan 2014 11:41:53 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1W4qkF-0008Dm-JP; Sun, 19 Jan 2014 11:40:51 +0000 Received: from 71-19-161-253.dedicated.allstream.net ([71.19.161.253] helo=nsa.nbspaymentsolutions.com) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1W12KQ-0005HC-SP; Wed, 08 Jan 2014 23:14:33 +0000 Received: from ps-bpringlemeir1.nbspaymentsolutions.com (unknown [172.20.177.82]) by nsa.nbspaymentsolutions.com (Postfix) with ESMTP id C91823FC486; Wed, 8 Jan 2014 18:16:33 -0500 (EST) From: Bill Pringlemeir To: linux-mtd@lists.infradead.org, linux-arm-kernel@lists.infradead.org Subject: [RFC 1/5] mtd:fsl_nfc: Nand flash controller for VF610, MPC5125, etc. Date: Wed, 8 Jan 2014 18:07:17 -0500 Message-Id: <1389222441-4322-2-git-send-email-bpringlemeir@nbsps.com> X-Mailer: git-send-email 1.8.0.2 In-Reply-To: <1389222441-4322-1-git-send-email-bpringlemeir@nbsps.com> References: <87siupheou.fsf@nbsps.com> <1389222441-4322-1-git-send-email-bpringlemeir@nbsps.com> X-Bad-Reply: References and In-Reply-To but no 'Re:' in Subject. X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140108_181427_282564_CD241ECB X-CRM114-Status: GOOD ( 23.97 ) X-Spam-Score: -0.9 (/) X-Mailman-Approved-At: Sun, 19 Jan 2014 06:39:47 -0500 Cc: Jason.jin@freescale.com, b21989@freescale.com, Bill Pringlemeir X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.7 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Limitations: - Untested on MPC5125, MCF54418 and Kinetis K70. - DMA not used. - 2K pages or less. - No hardware ECC. Signed-off-by: Bill Pringlemeir --- drivers/mtd/nand/Kconfig | 12 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/fsl_nfc.c | 676 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 689 insertions(+) create mode 100644 drivers/mtd/nand/fsl_nfc.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 93ae6a6..7e0c695 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -449,6 +449,18 @@ config MTD_NAND_MPC5121_NFC This enables the driver for the NAND flash controller on the MPC5121 SoC. +config HAVE_NAND_FSL_NFC + bool + +config MTD_NAND_FSL_NFC + tristate "Support for Freescale NFC" + depends on HAVE_NAND_FSL_NFC + help + Enables support for NAND Flash controller on some Freescale + processors like the MPC5125, VF610, MCF54418, and Kinetis K70. + The driver supports a maximum 2k page size. + The driver currently does not support hardware ECC. + config MTD_NAND_MXC tristate "MXC NAND support" depends on ARCH_MXC diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 542b568..116f827 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o +obj-$(CONFIG_MTD_NAND_FSL_NFC) += fsl_nfc.o obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o obj-$(CONFIG_MTD_NAND_SLC_LPC32XX) += lpc32xx_slc.o obj-$(CONFIG_MTD_NAND_MLC_LPC32XX) += lpc32xx_mlc.o diff --git a/drivers/mtd/nand/fsl_nfc.c b/drivers/mtd/nand/fsl_nfc.c new file mode 100644 index 0000000..eb4e353 --- /dev/null +++ b/drivers/mtd/nand/fsl_nfc.c @@ -0,0 +1,676 @@ +/* + * Copyright 2009-2012 Freescale Semiconductor, Inc. + * + * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver. + * Jason ported to M54418TWR and MVFA5. + * Authors: Shaohui Xie + * Jason Jin + * + * Based on original driver mpc5121_nfc.c. + * + * This 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; either version 2 of the License, or + * (at your option) any later version. + * + * Limitations: + * - Untested on MPC5125 and M54418. + * - DMA not used. + * - 2K pages or less. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "fsl_nfc" + +/* Register Offsets */ +#define NFC_FLASH_CMD1 0x3F00 +#define NFC_FLASH_CMD2 0x3F04 +#define NFC_COL_ADDR 0x3F08 +#define NFC_ROW_ADDR 0x3F0c +#define NFC_ROW_ADDR_INC 0x3F14 +#define NFC_FLASH_STATUS1 0x3F18 +#define NFC_FLASH_STATUS2 0x3F1c +#define NFC_CACHE_SWAP 0x3F28 +#define NFC_SECTOR_SIZE 0x3F2c +#define NFC_FLASH_CONFIG 0x3F30 +#define NFC_IRQ_STATUS 0x3F38 + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x1000) + +#define PAGE_2K 0x0800 +#define OOB_64 0x0040 + +/* NFC_CMD2[CODE] values. See section: + - 31.4.7 Flash Command Code Description, Vybrid manual + - 23.8.6 Flash Command Sequencer, MPC5125 manual + + Briefly these are bitmasks of controller cycles. +*/ +#define READ_PAGE_CMD_CODE 0x7EE0 +#define PROGRAM_PAGE_CMD_CODE 0x7FC0 +#define ERASE_CMD_CODE 0x4EC0 +#define READ_ID_CMD_CODE 0x4804 +#define RESET_CMD_CODE 0x4040 +#define STATUS_READ_CMD_CODE 0x4068 + +/* NFC ECC mode define */ +#define ECC_BYPASS 0 + +/*** Register Mask and bit definitions */ + +/* NFC_FLASH_CMD1 Field */ +#define CMD_BYTE2_MASK 0xFF000000 +#define CMD_BYTE2_SHIFT 24 + +/* NFC_FLASH_CM2 Field */ +#define CMD_BYTE1_MASK 0xFF000000 +#define CMD_BYTE1_SHIFT 24 +#define CMD_CODE_MASK 0x00FFFF00 +#define CMD_CODE_SHIFT 8 +#define BUFNO_MASK 0x00000006 +#define BUFNO_SHIFT 1 +#define START_BIT (1<<0) + +/* NFC_COL_ADDR Field */ +#define COL_ADDR_MASK 0x0000FFFF +#define COL_ADDR_SHIFT 0 + +/* NFC_ROW_ADDR Field */ +#define ROW_ADDR_MASK 0x00FFFFFF +#define ROW_ADDR_SHIFT 0 +#define ROW_ADDR_CHIP_SEL_RB_MASK 0xF0000000 +#define ROW_ADDR_CHIP_SEL_RB_SHIFT 28 +#define ROW_ADDR_CHIP_SEL_MASK 0x0F000000 +#define ROW_ADDR_CHIP_SEL_SHIFT 24 + +/* NFC_FLASH_STATUS2 Field */ +#define STATUS_BYTE1_MASK 0x000000FF + +/* NFC_FLASH_CONFIG Field */ +#define CONFIG_ECC_SRAM_REQ_BIT (1<<21) +#define CONFIG_DMA_REQ_BIT (1<<20) +#define CONFIG_ECC_MODE_MASK 0x000E0000 +#define CONFIG_ECC_MODE_SHIFT 17 +#define CONFIG_FAST_FLASH_BIT (1<<16) +#define CONFIG_16BIT (1<<7) +#define CONFIG_BOOT_MODE_BIT (1<<6) +#define CONFIG_ADDR_AUTO_INCR_BIT (1<<5) +#define CONFIG_BUFNO_AUTO_INCR_BIT (1<<4) +#define CONFIG_PAGE_CNT_MASK 0xF +#define CONFIG_PAGE_CNT_SHIFT 0 + +/* NFC_IRQ_STATUS Field */ +#define IDLE_IRQ_BIT (1<<29) +#define IDLE_EN_BIT (1<<20) +#define CMD_DONE_CLEAR_BIT (1<<18) +#define IDLE_CLEAR_BIT (1<<17) + +#define NFC_TIMEOUT (HZ) + +struct fsl_nfc { + struct mtd_info mtd; + struct nand_chip chip; + struct device *dev; + void __iomem *regs; + wait_queue_head_t irq_waitq; + uint column; + int spareonly; + int page; + /* Status and ID are in alternate locations. */ + int alt_buf; +#define ALT_BUF_ID 1 +#define ALT_BUF_STAT 2 + struct clk *clk; +}; +#define mtd_to_nfc(_mtd) container_of(_mtd, struct fsl_nfc, mtd) + +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 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, + .offs = 11, + .len = 4, + .veroffs = 15, + .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, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +static u32 nfc_read(struct mtd_info *mtd, uint reg) +{ + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + if (reg == NFC_FLASH_STATUS1 || + reg == NFC_FLASH_STATUS2 || + reg == NFC_IRQ_STATUS) + return __raw_readl(nfc->regs + reg); + /* Gang read/writes together for most registers. */ + else + return *(u32 *)(nfc->regs + reg); +} + +static void nfc_write(struct mtd_info *mtd, uint reg, u32 val) +{ + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + + if (reg == NFC_FLASH_STATUS1 || + reg == NFC_FLASH_STATUS2 || + reg == NFC_IRQ_STATUS) + __raw_writel(val, nfc->regs + reg); + /* Gang read/writes together for most registers. */ + else + *(u32 *)(nfc->regs + reg) = val; +} + +static void nfc_set(struct mtd_info *mtd, uint reg, u32 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); +} + +static void nfc_clear(struct mtd_info *mtd, uint reg, u32 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); +} + +static void nfc_set_field(struct mtd_info *mtd, u32 reg, + u32 mask, u32 shift, u32 val) +{ + nfc_write(mtd, reg, (nfc_read(mtd, reg) & (~mask)) | val << shift); +} + +/* Clear flags for upcoming command */ +static void nfc_clear_status(struct mtd_info *mtd) +{ + nfc_set(mtd, NFC_IRQ_STATUS, CMD_DONE_CLEAR_BIT); + nfc_set(mtd, NFC_IRQ_STATUS, IDLE_CLEAR_BIT); +} + +/* Wait for complete operation */ +static void nfc_done(struct mtd_info *mtd) +{ + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + int rv; + + nfc_set(mtd, NFC_IRQ_STATUS, IDLE_EN_BIT); + /* CMD2 is almost non-volatile, START_BIT is not */ + barrier(); + nfc_set(mtd, NFC_FLASH_CMD2, START_BIT); + barrier(); + + if (!(nfc_read(mtd, NFC_IRQ_STATUS) & IDLE_IRQ_BIT)) { + rv = wait_event_timeout(nfc->irq_waitq, + (nfc_read(mtd, NFC_IRQ_STATUS) & IDLE_IRQ_BIT), + NFC_TIMEOUT); + if (!rv) + dev_warn(nfc->dev, "Timeout while waiting for BUSY.\n"); + } + nfc_clear_status(mtd); +} + +static u8 nfc_get_id(struct mtd_info *mtd, int col) +{ + u32 flash_id; + + if (col < 4) { + flash_id = nfc_read(mtd, NFC_FLASH_STATUS1); + return (flash_id >> (3-col)*8) & 0xff; + } else { + flash_id = nfc_read(mtd, NFC_FLASH_STATUS2); + return flash_id >> 24; + } +} + +static u8 nfc_get_status(struct mtd_info *mtd) +{ + return nfc_read(mtd, NFC_FLASH_STATUS2) & STATUS_BYTE1_MASK; +} + +/* Single command */ +static void nfc_send_command(struct mtd_info *mtd, u32 cmd_byte1, + u32 cmd_code) +{ + nfc_clear_status(mtd); + + nfc_set_field(mtd, NFC_FLASH_CMD2, CMD_BYTE1_MASK, + CMD_BYTE1_SHIFT, cmd_byte1); + + nfc_clear(mtd, NFC_FLASH_CMD2, BUFNO_MASK); + + nfc_set_field(mtd, NFC_FLASH_CMD2, CMD_CODE_MASK, + CMD_CODE_SHIFT, cmd_code); +} + +/* Two commands */ +static void nfc_send_commands(struct mtd_info *mtd, u32 cmd_byte1, + u32 cmd_byte2, u32 cmd_code) +{ + nfc_send_command(mtd, cmd_byte1, cmd_code); + + nfc_set_field(mtd, NFC_FLASH_CMD1, CMD_BYTE2_MASK, + CMD_BYTE2_SHIFT, cmd_byte2); +} + +static irqreturn_t nfc_irq(int irq, void *data) +{ + struct mtd_info *mtd = data; + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + + nfc_clear(mtd, NFC_IRQ_STATUS, IDLE_EN_BIT); + wake_up(&nfc->irq_waitq); + + return IRQ_HANDLED; +} + +static void nfc_addr_cycle(struct mtd_info *mtd, int column, int page) +{ + if (column != -1) { + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + if (nfc->chip.options | NAND_BUSWIDTH_16) + column = column/2; + nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK, + COL_ADDR_SHIFT, column); + } + if (page != -1) + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK, + ROW_ADDR_SHIFT, page); +} + +/* Send command to NAND chip */ +static void nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + + nfc->column = max(column, 0); + nfc->spareonly = 0; + nfc->alt_buf = 0; + + switch (command) { + case NAND_CMD_PAGEPROG: + nfc->page = -1; + nfc_send_commands(mtd, NAND_CMD_SEQIN, + command, PROGRAM_PAGE_CMD_CODE); + nfc_addr_cycle(mtd, column, page); + break; + + case NAND_CMD_RESET: + nfc_send_command(mtd, command, RESET_CMD_CODE); + break; + /* + * NFC does not support sub-page reads and writes, + * so emulate them using full page transfers. + */ + case NAND_CMD_READOOB: + nfc->spareonly = 1; + case NAND_CMD_SEQIN: /* Pre-read for partial writes. */ + case NAND_CMD_READ0: + column = 0; + /* Already read? */ + if (nfc->page == page) + return; + nfc->page = page; + nfc_send_commands(mtd, NAND_CMD_READ0, + NAND_CMD_READSTART, READ_PAGE_CMD_CODE); + nfc_addr_cycle(mtd, column, page); + break; + + case NAND_CMD_ERASE1: + if (nfc->page == page) + nfc->page = -1; + nfc_send_commands(mtd, command, + NAND_CMD_ERASE2, ERASE_CMD_CODE); + nfc_addr_cycle(mtd, column, page); + break; + + case NAND_CMD_READID: + nfc->alt_buf = ALT_BUF_ID; + nfc_send_command(mtd, command, READ_ID_CMD_CODE); + break; + + case NAND_CMD_STATUS: + nfc->alt_buf = ALT_BUF_STAT; + nfc_send_command(mtd, command, STATUS_READ_CMD_CODE); + break; + default: + return; + } + + nfc_done(mtd); +} + +static void nfc_read_spare(struct mtd_info *mtd, void *buf, int len) +{ + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + + len = min(mtd->oobsize, (uint)len); + if (len > 0) + memcpy(buf, nfc->regs + mtd->writesize, len); +} + +/* Read data from NFC buffers */ +static void nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + uint c = nfc->column; + uint l; + + /* Handle main area */ + if (!nfc->spareonly) { + + l = min((uint)len, mtd->writesize - c); + nfc->column += l; + + if (!nfc->alt_buf) + memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, l); + else + if (nfc->alt_buf & ALT_BUF_ID) + *buf = nfc_get_id(mtd, c); + else + *buf = nfc_get_status(mtd); + buf += l; + len -= l; + } + + /* Handle spare area access */ + if (len) { + nfc->column += len; + nfc_read_spare(mtd, buf, len); + } +} + +/* Write data to NFC buffers */ +static void nfc_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + uint c = nfc->column; + uint l; + + l = min((uint)len, mtd->writesize + mtd->oobsize - c); + nfc->column += l; + + memcpy(nfc->regs + NFC_MAIN_AREA(0) + c, buf, l); +} + +/* Read byte from NFC buffers */ +static u8 nfc_read_byte(struct mtd_info *mtd) +{ + u8 tmp; + nfc_read_buf(mtd, &tmp, sizeof(tmp)); + return tmp; +} + +/* Read word from NFC buffers */ +static u16 nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); + return tmp; +} + +/* If not provided, upper layers apply a fixed delay. */ +static int nfc_dev_ready(struct mtd_info *mtd) +{ + /* NFC handles R/B internally; always ready. */ + return 1; +} + +/* Vybrid only. MPC5125 has full RB and four CS. Assume boot loader + * has set this register for now. + */ +static void +nfc_select_chip(struct mtd_info *mtd, int chip) +{ +#ifdef CONFIG_SOC_VF610 + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_RB_MASK, + ROW_ADDR_CHIP_SEL_RB_SHIFT, 1); + + if (chip == 0) + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_MASK, + ROW_ADDR_CHIP_SEL_SHIFT, 1); + else if (chip == 1) + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_MASK, + ROW_ADDR_CHIP_SEL_SHIFT, 2); + else + nfc_clear(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_MASK); +#endif +} + +struct nfc_config { + int width; + int flash_bbt; + u32 clkrate; +}; + +#ifdef CONFIG_OF_MTD +static struct of_device_id nfc_dt_ids[] = { + { .compatible = "fsl,vf610-nfc" }, + { .compatible = "fsl,mpc5125-nfc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, nfc_dt_ids); + +static int __init nfc_probe_dt(struct device *dev, struct nfc_config *cfg) +{ + struct device_node *np = dev->of_node; + int buswidth; + u32 clkrate; + + memset(cfg, 0, sizeof(*cfg)); + + if (!np) + return 1; + + cfg->flash_bbt = of_get_nand_on_flash_bbt(np); + + if (!of_property_read_u32(np, "clock-frequency", &clkrate)) + cfg->clkrate = clkrate; + + buswidth = of_get_nand_bus_width(np); + if (buswidth < 0) + return buswidth; + + cfg->width = buswidth; + + return 0; +} +#else +static int __init nfc_probe_dt(struct device *dev, struct nfc_config *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + return 0; +} +#endif + +static int nfc_probe(struct platform_device *pdev) +{ + struct fsl_nfc *nfc; + struct resource *res; + struct mtd_info *mtd; + struct nand_chip *chip; + struct nfc_config cfg; + int err = 0; + int page_sz; + int irq; + + nfc = devm_kzalloc(&pdev->dev, sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return -ENOMEM; + + nfc->dev = &pdev->dev; + nfc->page = -1; + mtd = &nfc->mtd; + chip = &nfc->chip; + + mtd->priv = chip; + mtd->owner = THIS_MODULE; + mtd->dev.parent = nfc->dev; + mtd->name = DRV_NAME; + + err = nfc_probe_dt(nfc->dev, &cfg); + if (err) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->regs = devm_ioremap_resource(nfc->dev, res); + if (IS_ERR(nfc->regs)) + return PTR_ERR(nfc->regs); + + nfc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(nfc->clk)) + return PTR_ERR(nfc->clk); + + if (cfg.clkrate && clk_set_rate(nfc->clk, cfg.clkrate)) { + dev_err(nfc->dev, "Clock rate not set."); + return -EINVAL; + } + + err = clk_prepare_enable(nfc->clk); + if (err) { + dev_err(nfc->dev, "Unable to enable clock!\n"); + return err; + } + + if (cfg.width == 16) { + chip->options |= NAND_BUSWIDTH_16; + nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); + } else { + chip->options &= ~NAND_BUSWIDTH_16; + nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); + } + + chip->dev_ready = nfc_dev_ready; + chip->cmdfunc = nfc_command; + chip->read_byte = nfc_read_byte; + chip->read_word = nfc_read_word; + chip->read_buf = nfc_read_buf; + chip->write_buf = nfc_write_buf; + chip->select_chip = nfc_select_chip; + + /* Bad block options. */ + if (cfg.flash_bbt) + chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_CREATE; + + /* Default to software ECC until flash ID. */ + nfc_set_field(mtd, NFC_FLASH_CONFIG, + CONFIG_ECC_MODE_MASK, + CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); + + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + + init_waitqueue_head(&nfc->irq_waitq); + + err = devm_request_irq(nfc->dev, irq, nfc_irq, 0, DRV_NAME, mtd); + if (err) { + dev_err(nfc->dev, "Error requesting IRQ!\n"); + goto error; + } + + page_sz = PAGE_2K + OOB_64; + page_sz += cfg.width == 16 ? 1 : 0; + nfc_write(mtd, NFC_SECTOR_SIZE, page_sz); + + /* Set configuration register. */ + nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); + nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT); + nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BOOT_MODE_BIT); + nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_DMA_REQ_BIT); + nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_FAST_FLASH_BIT); + + /* PAGE_CNT = 1 */ + nfc_set_field(mtd, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK, + CONFIG_PAGE_CNT_SHIFT, 1); + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1, NULL)) { + err = -ENXIO; + goto error; + } + + chip->ecc.mode = NAND_ECC_SOFT; /* default */ + + page_sz = mtd->writesize + mtd->oobsize; + + /* Single buffer only, max 256 OOB minus ECC status */ + if (page_sz > PAGE_2K + 256 - 8) { + dev_err(nfc->dev, "Unsupported flash size\n"); + err = -ENXIO; + goto error; + } + page_sz += cfg.width == 16 ? 1 : 0; + nfc_write(mtd, NFC_SECTOR_SIZE, page_sz); + + /* second phase scan */ + if (nand_scan_tail(mtd)) { + err = -ENXIO; + goto error; + } + + /* Register device in MTD */ + mtd_device_parse_register(mtd, NULL, + &(struct mtd_part_parser_data){ + .of_node = pdev->dev.of_node, + }, + NULL, 0); + + platform_set_drvdata(pdev, mtd); + + return 0; + +error: + clk_disable_unprepare(nfc->clk); + return err; +} + +static int nfc_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct fsl_nfc *nfc = mtd_to_nfc(mtd); + + nand_release(mtd); + clk_disable_unprepare(nfc->clk); + return 0; +} + +static struct platform_driver nfc_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = nfc_dt_ids, + }, + .probe = nfc_probe, + .remove = nfc_remove, +}; + +module_platform_driver(nfc_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("FSL NFC MTD driver"); +MODULE_LICENSE("GPL");