[PATCH 1/2] mtd: nand: mxs-nand: Add i.MX6 support
Sascha Hauer
s.hauer at pengutronix.de
Mon Jul 22 06:28:04 EDT 2013
The i.MX6 uses the same GPMI NAND controller as i.MX23/28 do. This adds
i.MX6 support to the driver.
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
arch/arm/mach-imx/clk-imx6.c | 1 +
arch/arm/mach-imx/include/mach/imx6-regs.h | 2 +
drivers/dma/Kconfig | 2 +-
drivers/mtd/nand/Kconfig | 2 +-
drivers/mtd/nand/nand_mxs.c | 102 ++++++++++++++++++++++-------
5 files changed, 85 insertions(+), 24 deletions(-)
diff --git a/arch/arm/mach-imx/clk-imx6.c b/arch/arm/mach-imx/clk-imx6.c
index 3061779..4639c0b 100644
--- a/arch/arm/mach-imx/clk-imx6.c
+++ b/arch/arm/mach-imx/clk-imx6.c
@@ -303,6 +303,7 @@ static int imx6_ccm_probe(struct device_d *dev)
clkdev_add_physbase(clks[ahb], MX6_SATA_BASE_ADDR, NULL);
clkdev_add_physbase(clks[usbphy1], MX6_USBPHY1_BASE_ADDR, NULL);
clkdev_add_physbase(clks[usbphy2], MX6_USBPHY2_BASE_ADDR, NULL);
+ clkdev_add_physbase(clks[enfc_podf], MX6_GPMI_BASE_ADDR, NULL);
writel(0xffffffff, ccm_base + CCGR0);
writel(0xffffffff, ccm_base + CCGR1);
diff --git a/arch/arm/mach-imx/include/mach/imx6-regs.h b/arch/arm/mach-imx/include/mach/imx6-regs.h
index d947aa6..833280a 100644
--- a/arch/arm/mach-imx/include/mach/imx6-regs.h
+++ b/arch/arm/mach-imx/include/mach/imx6-regs.h
@@ -1,6 +1,8 @@
#ifndef __MACH_IMX6_REGS_H
#define __MACH_IMX6_REGS_H
+#define MX6_GPMI_BASE_ADDR 0x00112000
+
#define MX6_AIPS1_ARB_BASE_ADDR 0x02000000
#define MX6_AIPS2_ARB_BASE_ADDR 0x02100000
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index c43c93e..c75fc8b 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -2,7 +2,7 @@ menu "DMA support"
config MXS_APBH_DMA
tristate "MXS APBH DMA ENGINE"
- depends on ARCH_IMX23 || ARCH_IMX28
+ depends on ARCH_IMX23 || ARCH_IMX28 || ARCH_IMX6
select STMP_DEVICE
help
Experimental!
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index de7b03f..04fe3c8 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -80,7 +80,7 @@ config NAND_IMX_BBM
config NAND_MXS
bool
select NAND_BBT
- prompt "i.MX23/28 NAND driver"
+ prompt "i.MX23/28/6 NAND driver"
depends on MXS_APBH_DMA
config NAND_OMAP_GPMC
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index 20c61bb..68eb040 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <of_mtd.h>
#include <common.h>
#include <malloc.h>
#include <errno.h>
@@ -121,12 +122,14 @@
#define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16
#define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12)
#define BCH_FLASHLAYOUT0_ECC0_OFFSET 12
+#define IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET 11
#define BCH_FLASH0LAYOUT1 0x00000090
#define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16)
#define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16
#define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12)
#define BCH_FLASHLAYOUT1_ECCN_OFFSET 12
+#define IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET 11
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
@@ -137,12 +140,19 @@
#define MXS_NAND_BCH_TIMEOUT 10000
+enum gpmi_type {
+ GPMI_MXS,
+ GPMI_IMX6,
+};
+
struct mxs_nand_info {
struct nand_chip nand_chip;
void __iomem *io_base;
void __iomem *bch_base;
struct clk *clk;
struct mtd_info mtd;
+ enum gpmi_type type;
+ int dma_channel_base;
u32 version;
int cur_chip;
@@ -171,6 +181,11 @@ struct mxs_nand_info {
struct nand_ecclayout fake_ecc_layout;
+static inline int mxs_nand_is_imx6(struct mxs_nand_info *info)
+{
+ return info->type == GPMI_IMX6;
+}
+
static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
{
struct mxs_dma_desc *desc;
@@ -332,7 +347,7 @@ static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl)
struct nand_chip *nand = mtd->priv;
struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d;
- uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret;
/*
@@ -485,7 +500,7 @@ static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length)
struct nand_chip *nand = mtd->priv;
struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d;
- uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret;
if (length > NAND_MAX_PAGESIZE) {
@@ -563,7 +578,7 @@ static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
struct nand_chip *nand = mtd->priv;
struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d;
- uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret;
if (length > NAND_MAX_PAGESIZE) {
@@ -623,7 +638,7 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand,
{
struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d;
- uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
uint32_t corrected = 0, failed = 0;
uint8_t *status;
int i, ret;
@@ -767,7 +782,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd,
{
struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d;
- uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip;
+ uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret = 0;
memcpy(nand_info->data_buf, buf, mtd->writesize);
@@ -1043,7 +1058,7 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)
struct nand_chip *nand = mtd->priv;
struct mxs_nand_info *nand_info = nand->priv;
void __iomem *bch_regs = nand_info->bch_base;
- uint32_t tmp;
+ uint32_t layout0, layout1;
int ret;
/* Reset BCH. Don't use SFTRST on MX23 due to Errata #2847 */
@@ -1052,21 +1067,36 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd)
if (ret)
return ret;
- /* Configure layout 0 */
- tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1)
- << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
- tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
- tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
- << BCH_FLASHLAYOUT0_ECC0_OFFSET;
- tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
- writel(tmp, bch_regs + BCH_FLASH0LAYOUT0);
-
- tmp = (mtd->writesize + mtd->oobsize)
- << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
- tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
- << BCH_FLASHLAYOUT1_ECCN_OFFSET;
- tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
- writel(tmp, bch_regs + BCH_FLASH0LAYOUT1);
+ if (mxs_nand_is_imx6(nand_info)) {
+ layout0 = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1)
+ << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET |
+ MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET |
+ (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
+ << IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET |
+ MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2;
+
+ layout1 = (mtd->writesize + mtd->oobsize)
+ << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET |
+ (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
+ << IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET |
+ MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2;
+ } else {
+ layout0 = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1)
+ << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET |
+ MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET |
+ (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
+ << BCH_FLASHLAYOUT0_ECC0_OFFSET |
+ MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
+
+ layout1 = (mtd->writesize + mtd->oobsize)
+ << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET |
+ (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
+ << BCH_FLASHLAYOUT1_ECCN_OFFSET |
+ MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
+ }
+
+ writel(layout0, bch_regs + BCH_FLASH0LAYOUT0);
+ writel(layout1, bch_regs + BCH_FLASH0LAYOUT1);
/* Set *all* chip selects to use layout 0 */
writel(0, bch_regs + BCH_LAYOUTSELECT);
@@ -1189,14 +1219,20 @@ static int mxs_nand_probe(struct device_d *dev)
struct mxs_nand_info *nand_info;
struct nand_chip *nand;
struct mtd_info *mtd;
+ enum gpmi_type type;
int err;
+ err = dev_get_drvdata(dev, (unsigned long *)&type);
+ if (err)
+ type = GPMI_MXS;
+
nand_info = kzalloc(sizeof(struct mxs_nand_info), GFP_KERNEL);
if (!nand_info) {
printf("MXS NAND: Failed to allocate private data\n");
return -ENOMEM;
}
+ nand_info->type = type;
nand_info->io_base = dev_request_mem_region(dev, 0);
nand_info->bch_base = dev_request_mem_region(dev, 1);
@@ -1204,7 +1240,13 @@ static int mxs_nand_probe(struct device_d *dev)
if (IS_ERR(nand_info->clk))
return PTR_ERR(nand_info->clk);
- clk_enable(nand_info->clk);
+ if (mxs_nand_is_imx6(nand_info)) {
+ clk_set_rate(nand_info->clk, 96000000);
+ clk_enable(nand_info->clk);
+ nand_info->dma_channel_base = 0;
+ } else {
+ nand_info->dma_channel_base = MXS_DMA_CHANNEL_AHB_APBH_GPMI0;
+ }
err = mxs_nand_alloc_buffers(nand_info);
if (err)
@@ -1268,9 +1310,25 @@ err1:
return err;
}
+static __maybe_unused struct of_device_id gpmi_dt_ids[] = {
+ {
+ .compatible = "fsl,imx23-gpmi-nand",
+ .data = GPMI_MXS,
+ }, {
+ .compatible = "fsl,imx28-gpmi-nand",
+ .data = GPMI_MXS,
+ }, {
+ .compatible = "fsl,imx6q-gpmi-nand",
+ .data = GPMI_IMX6,
+ }, {
+ /* sentinel */
+ }
+};
+
static struct driver_d mxs_nand_driver = {
.name = "mxs_nand",
.probe = mxs_nand_probe,
+ .of_compatible = DRV_OF_COMPAT(gpmi_dt_ids),
};
device_platform_driver(mxs_nand_driver);
--
1.8.3.2
More information about the barebox
mailing list