From patchwork Wed Jun 29 08:24:37 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Huang Shijie X-Patchwork-Id: 927452 Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by demeter2.kernel.org (8.14.4/8.14.4) with ESMTP id p5T88tmX032749 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Wed, 29 Jun 2011 08:09:16 GMT Received: from canuck.infradead.org ([2001:4978:20e::1]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1Qbpov-00040k-Ac; Wed, 29 Jun 2011 08:08:26 +0000 Received: from localhost ([127.0.0.1] helo=canuck.infradead.org) by canuck.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1Qbpn9-0006nc-Ka; Wed, 29 Jun 2011 08:06:35 +0000 Received: from va3ehsobe006.messaging.microsoft.com ([216.32.180.16] helo=VA3EHSOBE009.bigfish.com) by canuck.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1Qbpm6-0006Z8-9I; Wed, 29 Jun 2011 08:05:37 +0000 Received: from mail56-va3-R.bigfish.com (10.7.14.242) by VA3EHSOBE009.bigfish.com (10.7.40.29) with Microsoft SMTP Server id 14.1.225.22; Wed, 29 Jun 2011 08:05:27 +0000 Received: from mail56-va3 (localhost.localdomain [127.0.0.1]) by mail56-va3-R.bigfish.com (Postfix) with ESMTP id 0C700A7830D; Wed, 29 Jun 2011 08:05:27 +0000 (UTC) X-SpamScore: 6 X-BigFish: VS6(zzc8kzz1202h1082kzz8275bhz2dh2a8h668h839h65h) X-Spam-TCS-SCL: 4:0 X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPVD:NLI; H:mail.freescale.net; RD:none; EFVD:NLI Received: from mail56-va3 (localhost.localdomain [127.0.0.1]) by mail56-va3 (MessageSwitch) id 1309334698939563_23198; Wed, 29 Jun 2011 08:04:58 +0000 (UTC) Received: from VA3EHSMHS017.bigfish.com (unknown [10.7.14.236]) by mail56-va3.bigfish.com (Postfix) with ESMTP id C2E671C383CA; Wed, 29 Jun 2011 08:04:43 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by VA3EHSMHS017.bigfish.com (10.7.99.27) with Microsoft SMTP Server (TLS) id 14.1.225.22; Wed, 29 Jun 2011 08:04:43 +0000 Received: from az33smr01.freescale.net (10.64.34.199) by 039-SN1MMR1-001.039d.mgd.msft.net (10.84.1.13) with Microsoft SMTP Server id 14.1.289.8; Wed, 29 Jun 2011 03:04:41 -0500 Received: from localhost.localdomain (shlinux2.ap.freescale.net [10.192.224.44]) by az33smr01.freescale.net (8.13.1/8.13.0) with ESMTP id p5T84WlG027020; Wed, 29 Jun 2011 03:04:39 -0500 (CDT) From: Huang Shijie To: Subject: [PATCH v7 2/3] MTD : add support for imx23 and imx28 Date: Wed, 29 Jun 2011 16:24:37 +0800 Message-ID: <1309335878-16678-3-git-send-email-b32955@freescale.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1309335878-16678-1-git-send-email-b32955@freescale.com> References: <1309335878-16678-1-git-send-email-b32955@freescale.com> MIME-Version: 1.0 X-OriginatorOrg: freescale.com X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110629_040531_203754_A066A336 X-CRM114-Status: GOOD ( 18.23 ) X-Spam-Score: -2.3 (--) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (-2.3 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [216.32.180.16 listed in list.dnswl.org] Cc: w.sang@pengutronix.de, thierry.nolf.barco@gmail.com, Huang Shijie , linux-mtd@lists.infradead.org, linux-arm-kernel@lists.infradead.org, LW@KARO-electronics.de X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Wed, 29 Jun 2011 08:09:17 +0000 (UTC) Add the implementation code for imx23 and imx28. The imx23 and imx28 share most of the same code. Signed-off-by: Huang Shijie --- drivers/mtd/nand/gpmi-nfc/bch-regs.h | 88 ++++++ drivers/mtd/nand/gpmi-nfc/gpmi-regs.h | 163 ++++++++++ drivers/mtd/nand/gpmi-nfc/hal-mxs.c | 554 +++++++++++++++++++++++++++++++++ 3 files changed, 805 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/gpmi-nfc/bch-regs.h create mode 100644 drivers/mtd/nand/gpmi-nfc/gpmi-regs.h create mode 100644 drivers/mtd/nand/gpmi-nfc/hal-mxs.c diff --git a/drivers/mtd/nand/gpmi-nfc/bch-regs.h b/drivers/mtd/nand/gpmi-nfc/bch-regs.h new file mode 100644 index 0000000..90af8cf --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/bch-regs.h @@ -0,0 +1,88 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __GPMI_NFC_BCH_REGS_H +#define __GPMI_NFC_BCH_REGS_H + +/*============================================================================*/ +#define HW_BCH_CTRL 0x00000000 +#define HW_BCH_CTRL_SET 0x00000004 +#define HW_BCH_CTRL_CLR 0x00000008 +#define HW_BCH_CTRL_TOG 0x0000000c + +#define BM_BCH_CTRL_COMPLETE_IRQ_EN (1 << 8) +#define BM_BCH_CTRL_COMPLETE_IRQ (1 << 0) + +/*============================================================================*/ +#define HW_BCH_STATUS0 0x00000010 +#define HW_BCH_MODE 0x00000020 +#define HW_BCH_ENCODEPTR 0x00000030 +#define HW_BCH_DATAPTR 0x00000040 +#define HW_BCH_METAPTR 0x00000050 +#define HW_BCH_LAYOUTSELECT 0x00000070 + +/*============================================================================*/ +#define HW_BCH_FLASH0LAYOUT0 0x00000080 + +#define BP_BCH_FLASH0LAYOUT0_NBLOCKS 24 +#define BM_BCH_FLASH0LAYOUT0_NBLOCKS (0xff << BP_BCH_FLASH0LAYOUT0_NBLOCKS) +#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & BM_BCH_FLASH0LAYOUT0_NBLOCKS) + +#define BP_BCH_FLASH0LAYOUT0_META_SIZE 16 +#define BM_BCH_FLASH0LAYOUT0_META_SIZE (0xff << BP_BCH_FLASH0LAYOUT0_META_SIZE) +#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE)\ + & BM_BCH_FLASH0LAYOUT0_META_SIZE) + +#define BP_BCH_FLASH0LAYOUT0_ECC0 12 +#define BM_BCH_FLASH0LAYOUT0_ECC0 (0xf << BP_BCH_FLASH0LAYOUT0_ECC0) +#define BF_BCH_FLASH0LAYOUT0_ECC0(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & BM_BCH_FLASH0LAYOUT0_ECC0) + +#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE 0 +#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) +#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)\ + & BM_BCH_FLASH0LAYOUT0_DATA0_SIZE) + +/*============================================================================*/ +#define HW_BCH_FLASH0LAYOUT1 0x00000090 + +#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE 16 +#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE \ + (0xffff << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) +#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) \ + & BM_BCH_FLASH0LAYOUT1_PAGE_SIZE) + +#define BP_BCH_FLASH0LAYOUT1_ECCN 12 +#define BM_BCH_FLASH0LAYOUT1_ECCN (0xf << BP_BCH_FLASH0LAYOUT1_ECCN) +#define BF_BCH_FLASH0LAYOUT1_ECCN(v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & BM_BCH_FLASH0LAYOUT1_ECCN) + +#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE 0 +#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE \ + (0xfff << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v) \ + (((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) \ + & BM_BCH_FLASH0LAYOUT1_DATAN_SIZE) +#endif diff --git a/drivers/mtd/nand/gpmi-nfc/gpmi-regs.h b/drivers/mtd/nand/gpmi-nfc/gpmi-regs.h new file mode 100644 index 0000000..d012c76 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/gpmi-regs.h @@ -0,0 +1,163 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright 2008-2011 Freescale Semiconductor, Inc. + * Copyright 2008 Embedded Alley Solutions, Inc. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __GPMI_NFC_GPMI_REGS_H +#define __GPMI_NFC_GPMI_REGS_H + +/*============================================================================*/ +#define HW_GPMI_CTRL0 0x00000000 +#define HW_GPMI_CTRL0_SET 0x00000004 +#define HW_GPMI_CTRL0_CLR 0x00000008 +#define HW_GPMI_CTRL0_TOG 0x0000000c + +#define BP_GPMI_CTRL0_COMMAND_MODE 24 +#define BM_GPMI_CTRL0_COMMAND_MODE (3 << BP_GPMI_CTRL0_COMMAND_MODE) +#define BF_GPMI_CTRL0_COMMAND_MODE(v) \ + (((v) << BP_GPMI_CTRL0_COMMAND_MODE) & BM_GPMI_CTRL0_COMMAND_MODE) +#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE 0x0 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ 0x1 +#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2 +#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY 0x3 + +#define BM_GPMI_CTRL0_WORD_LENGTH (1 << 23) +#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT 0x0 +#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT 0x1 + +/* different in CS between imx23 and imx28 */ +#define BP_GPMI_CTRL0_CS 20 +#define MX23_BM_GPMI_CTRL0_CS (3 << BP_GPMI_CTRL0_CS) +#define MX28_BM_GPMI_CTRL0_CS (7 << BP_GPMI_CTRL0_CS) +#define BF_GPMI_CTRL0_CS(v, x) (((v) << 20) & (GPMI_IS_MX23((x)) \ + ? MX23_BM_GPMI_CTRL0_CS \ + : MX28_BM_GPMI_CTRL0_CS)) + +#define BP_GPMI_CTRL0_ADDRESS 17 +#define BM_GPMI_CTRL0_ADDRESS (3 << BP_GPMI_CTRL0_ADDRESS) +#define BF_GPMI_CTRL0_ADDRESS(v) \ + (((v) << BP_GPMI_CTRL0_ADDRESS) & BM_GPMI_CTRL0_ADDRESS) +#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0 +#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE 0x1 +#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE 0x2 + +#define BM_GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__DISABLED 0x0 +#define BV_GPMI_CTRL0_ADDRESS_INCREMENT__ENABLED 0x1 + +#define BP_GPMI_CTRL0_XFER_COUNT 0 +#define BM_GPMI_CTRL0_XFER_COUNT (0xffff << BP_GPMI_CTRL0_XFER_COUNT) +#define BF_GPMI_CTRL0_XFER_COUNT(v) \ + (((v) << BP_GPMI_CTRL0_XFER_COUNT) & BM_GPMI_CTRL0_XFER_COUNT) + +/*============================================================================*/ +#define HW_GPMI_COMPARE 0x00000010 +/*============================================================================*/ +#define HW_GPMI_ECCCTRL 0x00000020 +#define HW_GPMI_ECCCTRL_SET 0x00000024 +#define HW_GPMI_ECCCTRL_CLR 0x00000028 +#define HW_GPMI_ECCCTRL_TOG 0x0000002c + +#define BP_GPMI_ECCCTRL_ECC_CMD 13 +#define BM_GPMI_ECCCTRL_ECC_CMD (3 << BP_GPMI_ECCCTRL_ECC_CMD) +#define BF_GPMI_ECCCTRL_ECC_CMD(v) \ + (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD) +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE 0x0 +#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE 0x1 + +#define BM_GPMI_ECCCTRL_ENABLE_ECC (1 << 12) +#define BV_GPMI_ECCCTRL_ENABLE_ECC__ENABLE 0x1 +#define BV_GPMI_ECCCTRL_ENABLE_ECC__DISABLE 0x0 + +#define BP_GPMI_ECCCTRL_BUFFER_MASK 0 +#define BM_GPMI_ECCCTRL_BUFFER_MASK (0x1ff << BP_GPMI_ECCCTRL_BUFFER_MASK) +#define BF_GPMI_ECCCTRL_BUFFER_MASK(v) \ + (((v) << BP_GPMI_ECCCTRL_BUFFER_MASK) & BM_GPMI_ECCCTRL_BUFFER_MASK) +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100 +#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE 0x1FF + +/*============================================================================*/ +#define HW_GPMI_ECCCOUNT 0x00000030 +#define HW_GPMI_PAYLOAD 0x00000040 +#define HW_GPMI_AUXILIARY 0x00000050 +/*============================================================================*/ +#define HW_GPMI_CTRL1 0x00000060 +#define HW_GPMI_CTRL1_SET 0x00000064 +#define HW_GPMI_CTRL1_CLR 0x00000068 +#define HW_GPMI_CTRL1_TOG 0x0000006c + +#define BM_GPMI_CTRL1_BCH_MODE (1 << 18) + +#define BP_GPMI_CTRL1_DLL_ENABLE 17 +#define BM_GPMI_CTRL1_DLL_ENABLE (1 << BP_GPMI_CTRL1_DLL_ENABLE) + +#define BP_GPMI_CTRL1_HALF_PERIOD 16 +#define BM_GPMI_CTRL1_HALF_PERIOD (1 << BP_GPMI_CTRL1_HALF_PERIOD) + +#define BP_GPMI_CTRL1_RDN_DELAY 12 +#define BM_GPMI_CTRL1_RDN_DELAY (0xf << BP_GPMI_CTRL1_RDN_DELAY) +#define BF_GPMI_CTRL1_RDN_DELAY(v) \ + (((v) << BP_GPMI_CTRL1_RDN_DELAY) & BM_GPMI_CTRL1_RDN_DELAY) + +#define BM_GPMI_CTRL1_DEV_RESET (1 << 3) +#define BV_GPMI_CTRL1_DEV_RESET__ENABLED 0x0 +#define BV_GPMI_CTRL1_DEV_RESET__DISABLED 0x1 + +#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2) +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW 0x0 +#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1 + +#define BM_GPMI_CTRL1_CAMERA_MODE (1 << 1) +#define BV_GPMI_CTRL1_GPMI_MODE__NAND 0x0 +#define BV_GPMI_CTRL1_GPMI_MODE__ATA 0x1 + +#define BM_GPMI_CTRL1_GPMI_MODE (1 << 0) + +/*============================================================================*/ +#define HW_GPMI_TIMING0 0x00000070 + +#define BP_GPMI_TIMING0_ADDRESS_SETUP 16 +#define BM_GPMI_TIMING0_ADDRESS_SETUP (0xff << BP_GPMI_TIMING0_ADDRESS_SETUP) +#define BF_GPMI_TIMING0_ADDRESS_SETUP(v) \ + (((v) << BP_GPMI_TIMING0_ADDRESS_SETUP) & BM_GPMI_TIMING0_ADDRESS_SETUP) + +#define BP_GPMI_TIMING0_DATA_HOLD 8 +#define BM_GPMI_TIMING0_DATA_HOLD (0xff << BP_GPMI_TIMING0_DATA_HOLD) +#define BF_GPMI_TIMING0_DATA_HOLD(v) \ + (((v) << BP_GPMI_TIMING0_DATA_HOLD) & BM_GPMI_TIMING0_DATA_HOLD) + +#define BP_GPMI_TIMING0_DATA_SETUP 0 +#define BM_GPMI_TIMING0_DATA_SETUP (0xff << BP_GPMI_TIMING0_DATA_SETUP) +#define BF_GPMI_TIMING0_DATA_SETUP(v) \ + (((v) << BP_GPMI_TIMING0_DATA_SETUP) & BM_GPMI_TIMING0_DATA_SETUP) + +/*============================================================================*/ +#define HW_GPMI_TIMING1 0x00000080 +#define HW_GPMI_TIMING2 0x00000090 +#define HW_GPMI_DATA 0x000000a0 +/*============================ MX28 uses this to detect READY ================*/ +#define HW_GPMI_STAT 0x000000b0 +#define MX28_BP_GPMI_STAT_READY_BUSY 24 +#define MX28_BM_GPMI_STAT_READY_BUSY (0xff << MX28_BP_GPMI_STAT_READY_BUSY) +#define MX28_BF_GPMI_STAT_READY_BUSY(v) \ + (((v) << MX28_BP_GPMI_STAT_READY_BUSY) & MX28_BM_GPMI_STAT_READY_BUSY) +/*============================ MX23 uses this to detect READY ================*/ +#define HW_GPMI_DEBUG 0x000000c0 +#define MX23_BP_GPMI_DEBUG_READY0 28 +#define MX23_BM_GPMI_DEBUG_READY0 (1 << MX23_BP_GPMI_DEBUG_READY0) +#endif diff --git a/drivers/mtd/nand/gpmi-nfc/hal-mxs.c b/drivers/mtd/nand/gpmi-nfc/hal-mxs.c new file mode 100644 index 0000000..4a94d44 --- /dev/null +++ b/drivers/mtd/nand/gpmi-nfc/hal-mxs.c @@ -0,0 +1,554 @@ +/* + * Freescale GPMI NFC NAND Flash Driver + * + * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. + * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "gpmi-nfc.h" +#include "gpmi-regs.h" +#include "bch-regs.h" + +static int init_hal(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + + /* Enable the clock */ + clk_enable(resources->clock); + + /* Reset the GPMI block. */ + mxs_reset_block(resources->gpmi_regs); + + /* Choose NAND mode. */ + __raw_writel(BM_GPMI_CTRL1_GPMI_MODE, + resources->gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Set the IRQ polarity. */ + __raw_writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Disable write protection. */ + __raw_writel(BM_GPMI_CTRL1_DEV_RESET, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Select BCH ECC. */ + __raw_writel(BM_GPMI_CTRL1_BCH_MODE, + resources->gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Disable the clock. */ + clk_disable(resources->clock); + return 0; +} + +/* Configures the NFC geometry for BCH. */ +static int set_geometry(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + struct nfc_geometry *nfc = &this->nfc_geometry; + unsigned int block_count; + unsigned int block_size; + unsigned int metadata_size; + unsigned int ecc_strength; + unsigned int page_size; + + if (common_nfc_set_geometry(this)) + return !0; + + block_count = nfc->ecc_chunk_count - 1; + block_size = nfc->ecc_chunk_size_in_bytes; + metadata_size = nfc->metadata_size_in_bytes; + ecc_strength = nfc->ecc_strength >> 1; + page_size = nfc->page_size_in_bytes; + + clk_enable(resources->clock); + + /* + * Reset the BCH block. Notice that we pass in true for the just_enable + * flag. This is because the soft reset for the version 0 BCH block + * doesn't work. If you try to soft reset the BCH block, it becomes + * unusable until the next hard reset. + */ + mxs_reset_block(resources->bch_regs); + + /* Configure layout 0. */ + __raw_writel(BF_BCH_FLASH0LAYOUT0_NBLOCKS(block_count) + | BF_BCH_FLASH0LAYOUT0_META_SIZE(metadata_size) + | BF_BCH_FLASH0LAYOUT0_ECC0(ecc_strength) + | BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block_size), + resources->bch_regs + HW_BCH_FLASH0LAYOUT0); + + __raw_writel(BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size) + | BF_BCH_FLASH0LAYOUT1_ECCN(ecc_strength) + | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size), + resources->bch_regs + HW_BCH_FLASH0LAYOUT1); + + /* Set *all* chip selects to use layout 0. */ + __raw_writel(0, resources->bch_regs + HW_BCH_LAYOUTSELECT); + + /* Enable interrupts. */ + __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, + resources->bch_regs + HW_BCH_CTRL_SET); + + clk_disable(resources->clock); + return 0; +} + +static int set_timing(struct gpmi_nfc_data *this, + const struct nand_timing *timing) +{ + struct nfc_hal *nfc = this->nfc; + + nfc->timing = *timing; + return 0; +} + +/** + * get_timing() - Retrieves the NFC hardware timing. + * + * @this: Per-device data. + * @clock_frequency_in_hz: The clock frequency, in Hz, during the current + * I/O transaction. If no I/O transaction is in + * progress, this is the clock frequency during the + * most recent I/O transaction. + * @hardware_timing: The hardware timing configuration in effect during + * the current I/O transaction. If no I/O transaction + * is in progress, this is the hardware timing + * configuration during the most recent I/O + * transaction. + */ +static void get_timing(struct gpmi_nfc_data *this, + unsigned long *clock_frequency_in_hz, + struct gpmi_nfc_hardware_timing *hardware_timing) +{ + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + unsigned char *gpmi_regs = resources->gpmi_regs; + uint32_t register_image; + + /* Return the clock frequency. */ + *clock_frequency_in_hz = nfc->clock_frequency_in_hz; + + /* We'll be reading the hardware, so let's enable the clock. */ + clk_enable(resources->clock); + + /* Retrieve the hardware timing. */ + register_image = __raw_readl(gpmi_regs + HW_GPMI_TIMING0); + + hardware_timing->data_setup_in_cycles = + (register_image & BM_GPMI_TIMING0_DATA_SETUP) >> + BP_GPMI_TIMING0_DATA_SETUP; + + hardware_timing->data_hold_in_cycles = + (register_image & BM_GPMI_TIMING0_DATA_HOLD) >> + BP_GPMI_TIMING0_DATA_HOLD; + + hardware_timing->address_setup_in_cycles = + (register_image & BM_GPMI_TIMING0_ADDRESS_SETUP) >> + BP_GPMI_TIMING0_ADDRESS_SETUP; + + register_image = __raw_readl(gpmi_regs + HW_GPMI_CTRL1); + + hardware_timing->use_half_periods = + (register_image & BM_GPMI_CTRL1_HALF_PERIOD) >> + BP_GPMI_CTRL1_HALF_PERIOD; + + hardware_timing->sample_delay_factor = + (register_image & BM_GPMI_CTRL1_RDN_DELAY) >> + BP_GPMI_CTRL1_RDN_DELAY; + + /* We're done reading the hardware, so disable the clock. */ + clk_disable(resources->clock); +} + +static void exit(struct gpmi_nfc_data *this) +{ +} + +/* Begin the I/O */ +static void begin(struct gpmi_nfc_data *this) +{ + struct resources *resources = &this->resources; + struct nfc_hal *nfc = this->nfc; + unsigned char *gpmi_regs = resources->gpmi_regs; + unsigned int clock_period_in_ns; + uint32_t register_image; + unsigned int dll_wait_time_in_us; + struct gpmi_nfc_hardware_timing hw; + + /* Enable the clock. */ + clk_enable(resources->clock); + + /* set the timing for imx23 */ + if (!GPMI_IS_MX23(this)) + return; + + /* Get the timing information we need. */ + nfc->clock_frequency_in_hz = clk_get_rate(resources->clock); + clock_period_in_ns = 1000000000 / nfc->clock_frequency_in_hz; + + gpmi_nfc_compute_hardware_timing(this, &hw); + + /* Set up all the simple timing parameters. */ + register_image = + BF_GPMI_TIMING0_ADDRESS_SETUP(hw.address_setup_in_cycles) | + BF_GPMI_TIMING0_DATA_HOLD(hw.data_hold_in_cycles) | + BF_GPMI_TIMING0_DATA_SETUP(hw.data_setup_in_cycles) ; + + __raw_writel(register_image, gpmi_regs + HW_GPMI_TIMING0); + + /* + * HEY - PAY ATTENTION! + * + * DLL_ENABLE must be set to zero when setting RDN_DELAY or HALF_PERIOD. + */ + __raw_writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* Clear out the DLL control fields. */ + __raw_writel(BM_GPMI_CTRL1_RDN_DELAY, gpmi_regs + HW_GPMI_CTRL1_CLR); + __raw_writel(BM_GPMI_CTRL1_HALF_PERIOD, gpmi_regs + HW_GPMI_CTRL1_CLR); + + /* If no sample delay is called for, return immediately. */ + if (!hw.sample_delay_factor) + return; + + /* Configure the HALF_PERIOD flag. */ + if (hw.use_half_periods) + __raw_writel(BM_GPMI_CTRL1_HALF_PERIOD, + gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Set the delay factor. */ + __raw_writel(BF_GPMI_CTRL1_RDN_DELAY(hw.sample_delay_factor), + gpmi_regs + HW_GPMI_CTRL1_SET); + + /* Enable the DLL. */ + __raw_writel(BM_GPMI_CTRL1_DLL_ENABLE, gpmi_regs + HW_GPMI_CTRL1_SET); + + /* + * After we enable the GPMI DLL, we have to wait 64 clock cycles before + * we can use the GPMI. + * + * Calculate the amount of time we need to wait, in microseconds. + */ + dll_wait_time_in_us = (clock_period_in_ns * 64) / 1000; + + if (!dll_wait_time_in_us) + dll_wait_time_in_us = 1; + + /* Wait for the DLL to settle. */ + udelay(dll_wait_time_in_us); +} + +static void end(struct gpmi_nfc_data *this) +{ + struct resources *r = &this->resources; + clk_disable(r->clock); +} + +/* Clears a BCH interrupt. */ +static void clear_bch(struct gpmi_nfc_data *this) +{ + struct resources *r = &this->resources; + __raw_writel(BM_BCH_CTRL_COMPLETE_IRQ, r->bch_regs + HW_BCH_CTRL_CLR); +} + +/* Returns the Ready/Busy status of the given chip. */ +static int is_ready(struct gpmi_nfc_data *this, unsigned chip) +{ + struct resources *r = &this->resources; + uint32_t mask; + uint32_t reg; + + if (GPMI_IS_MX23(this)) { + mask = MX23_BM_GPMI_DEBUG_READY0 << chip; + reg = __raw_readl(r->gpmi_regs + HW_GPMI_DEBUG); + } else if (GPMI_IS_MX28(this)) { + mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); + reg = __raw_readl(r->gpmi_regs + HW_GPMI_STAT); + } else + BUG(); + return !!(reg & mask); +} + +static int send_command(struct gpmi_nfc_data *this) +{ + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl; + u32 pio[3]; + + /* [1] send out the PIO words */ + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__WRITE) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(mil->current_chip, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_CLE) + | BM_GPMI_CTRL0_ADDRESS_INCREMENT + | BF_GPMI_CTRL0_XFER_COUNT(mil->command_length); + pio[1] = pio[2] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 0); + if (!desc) { + pr_info("step 1 error\n"); + return -1; + } + + /* [2] send out the COMMAND + ADDRESS string stored in @buffer */ + sgl = &mil->cmd_sgl; + + sg_init_one(sgl, mil->cmd_buffer, mil->command_length); + dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); + desc = channel->device->device_prep_slave_sg(channel, + sgl, 1, DMA_TO_DEVICE, 1); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + + /* [3] submit the DMA */ + this->dma_type = DMA_FOR_COMMAND; + start_dma_without_bch_irq(this, desc); + return 0; +} + +static int send_data(struct gpmi_nfc_data *this) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + uint32_t command_mode; + uint32_t address; + u32 pio[2]; + + /* [1] PIO */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(mil->current_chip, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(mil->upper_len); + pio[1] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 0); + if (!desc) { + pr_info("step 1 error\n"); + return -1; + } + + /* [2] send DMA request */ + prepare_data_dma(this, DMA_TO_DEVICE); + desc = channel->device->device_prep_slave_sg(channel, &mil->data_sgl, + 1, DMA_TO_DEVICE, 1); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + /* [3] submit the DMA */ + this->dma_type = DMA_FOR_WRITE_DATA; + start_dma_without_bch_irq(this, desc); + return 0; +} + +static int read_data(struct gpmi_nfc_data *this) +{ + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + u32 pio[2]; + + /* [1] : send PIO */ + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(BV_GPMI_CTRL0_COMMAND_MODE__READ) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(mil->current_chip, this) + | BF_GPMI_CTRL0_ADDRESS(BV_GPMI_CTRL0_ADDRESS__NAND_DATA) + | BF_GPMI_CTRL0_XFER_COUNT(mil->upper_len); + pio[1] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 0); + if (!desc) { + pr_info("step 1 error\n"); + return -1; + } + + /* [2] : send DMA request */ + prepare_data_dma(this, DMA_FROM_DEVICE); + desc = channel->device->device_prep_slave_sg(channel, &mil->data_sgl, + 1, DMA_FROM_DEVICE, 1); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + + /* [3] : submit the DMA */ + this->dma_type = DMA_FOR_READ_DATA; + start_dma_without_bch_irq(this, desc); + return 0; +} + +static int send_page(struct gpmi_nfc_data *this, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct nfc_geometry *geo = &this->nfc_geometry; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + int chip = mil->current_chip; + u32 pio[6]; + + /* A DMA descriptor that does an ECC page read. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE | + BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(0); + pio[1] = 0; + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); + pio[3] = geo->page_size_in_bytes; + pio[4] = payload; + pio[5] = auxiliary; + + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 0); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + this->dma_type = DMA_FOR_WRITE_ECC_PAGE; + return start_dma_with_bch_irq(this, desc); +} + +static int read_page(struct gpmi_nfc_data *this, + dma_addr_t payload, dma_addr_t auxiliary) +{ + struct nfc_geometry *geo = &this->nfc_geometry; + uint32_t command_mode; + uint32_t address; + uint32_t ecc_command; + uint32_t buffer_mask; + struct dma_async_tx_descriptor *desc; + struct dma_chan *channel = get_dma_chan(this); + struct mil *mil = &this->mil; + int chip = mil->current_chip; + u32 pio[6]; + + /* [1] Wait for the chip to report ready. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(0); + pio[1] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, 2, DMA_NONE, 0); + if (!desc) { + pr_info("step 1 error\n"); + return -1; + } + + /* [2] Enable the BCH block and read. */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + ecc_command = BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE; + buffer_mask = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE + | BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes); + + pio[1] = 0; + pio[2] = BM_GPMI_ECCCTRL_ENABLE_ECC + | BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) + | BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask); + pio[3] = geo->page_size_in_bytes; + pio[4] = payload; + pio[5] = auxiliary; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, + ARRAY_SIZE(pio), DMA_NONE, 1); + if (!desc) { + pr_info("step 2 error\n"); + return -1; + } + + /* [3] Disable the BCH block */ + command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY; + address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA; + + pio[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) + | BM_GPMI_CTRL0_WORD_LENGTH + | BF_GPMI_CTRL0_CS(chip, this) + | BF_GPMI_CTRL0_ADDRESS(address) + | BF_GPMI_CTRL0_XFER_COUNT(geo->page_size_in_bytes); + pio[1] = 0; + desc = channel->device->device_prep_slave_sg(channel, + (struct scatterlist *)pio, 2, DMA_NONE, 1); + if (!desc) { + pr_info("step 3 error\n"); + return -1; + } + + /* [4] submit the DMA */ + this->dma_type = DMA_FOR_READ_ECC_PAGE; + return start_dma_with_bch_irq(this, desc); +} + +struct nfc_hal gpmi_nfc_hal_mxs = { + .description = "GPMI and BCH for IMX23/IMX28", + .max_data_setup_cycles = (BM_GPMI_TIMING0_DATA_SETUP >> + BP_GPMI_TIMING0_DATA_SETUP), + .internal_data_setup_in_ns = 0, + .max_sample_delay_factor = (BM_GPMI_CTRL1_RDN_DELAY >> + BP_GPMI_CTRL1_RDN_DELAY), + .max_dll_clock_period_in_ns = 32, + .max_dll_delay_in_ns = 16, + .init = init_hal, + .set_geometry = set_geometry, + .set_timing = set_timing, + .get_timing = get_timing, + .exit = exit, + .begin = begin, + .end = end, + .clear_bch = clear_bch, + .is_ready = is_ready, + .send_command = send_command, + .read_data = read_data, + .send_data = send_data, + .read_page = read_page, + .send_page = send_page, +};