9
0
Fork 0

mtd: nand: mxs-nand: Add i.MX6 support

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@pengutronix.de>
This commit is contained in:
Sascha Hauer 2013-07-22 12:18:58 +02:00
parent a4f58f5665
commit 0f15dee78f
5 changed files with 84 additions and 23 deletions

View File

@ -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[ahb], MX6_SATA_BASE_ADDR, NULL);
clkdev_add_physbase(clks[usbphy1], MX6_USBPHY1_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[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 + CCGR0);
writel(0xffffffff, ccm_base + CCGR1); writel(0xffffffff, ccm_base + CCGR1);

View File

@ -1,6 +1,8 @@
#ifndef __MACH_IMX6_REGS_H #ifndef __MACH_IMX6_REGS_H
#define __MACH_IMX6_REGS_H #define __MACH_IMX6_REGS_H
#define MX6_GPMI_BASE_ADDR 0x00112000
#define MX6_AIPS1_ARB_BASE_ADDR 0x02000000 #define MX6_AIPS1_ARB_BASE_ADDR 0x02000000
#define MX6_AIPS2_ARB_BASE_ADDR 0x02100000 #define MX6_AIPS2_ARB_BASE_ADDR 0x02100000

View File

@ -2,7 +2,7 @@ menu "DMA support"
config MXS_APBH_DMA config MXS_APBH_DMA
tristate "MXS APBH DMA ENGINE" tristate "MXS APBH DMA ENGINE"
depends on ARCH_IMX23 || ARCH_IMX28 depends on ARCH_IMX23 || ARCH_IMX28 || ARCH_IMX6
select STMP_DEVICE select STMP_DEVICE
help help
Experimental! Experimental!

View File

@ -80,7 +80,7 @@ config NAND_IMX_BBM
config NAND_MXS config NAND_MXS
bool bool
select NAND_BBT select NAND_BBT
prompt "i.MX23/28 NAND driver" prompt "i.MX23/28/6 NAND driver"
depends on MXS_APBH_DMA depends on MXS_APBH_DMA
config NAND_OMAP_GPMC config NAND_OMAP_GPMC

View File

@ -23,6 +23,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <of_mtd.h>
#include <common.h> #include <common.h>
#include <malloc.h> #include <malloc.h>
#include <errno.h> #include <errno.h>
@ -121,12 +122,14 @@
#define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16 #define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16
#define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12) #define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12)
#define BCH_FLASHLAYOUT0_ECC0_OFFSET 12 #define BCH_FLASHLAYOUT0_ECC0_OFFSET 12
#define IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET 11
#define BCH_FLASH0LAYOUT1 0x00000090 #define BCH_FLASH0LAYOUT1 0x00000090
#define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16) #define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16)
#define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16 #define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16
#define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12) #define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12)
#define BCH_FLASHLAYOUT1_ECCN_OFFSET 12 #define BCH_FLASHLAYOUT1_ECCN_OFFSET 12
#define IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET 11
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 #define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
@ -137,12 +140,19 @@
#define MXS_NAND_BCH_TIMEOUT 10000 #define MXS_NAND_BCH_TIMEOUT 10000
enum gpmi_type {
GPMI_MXS,
GPMI_IMX6,
};
struct mxs_nand_info { struct mxs_nand_info {
struct nand_chip nand_chip; struct nand_chip nand_chip;
void __iomem *io_base; void __iomem *io_base;
void __iomem *bch_base; void __iomem *bch_base;
struct clk *clk; struct clk *clk;
struct mtd_info mtd; struct mtd_info mtd;
enum gpmi_type type;
int dma_channel_base;
u32 version; u32 version;
int cur_chip; int cur_chip;
@ -171,6 +181,11 @@ struct mxs_nand_info {
struct nand_ecclayout fake_ecc_layout; 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) static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
{ {
struct mxs_dma_desc *desc; 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 nand_chip *nand = mtd->priv;
struct mxs_nand_info *nand_info = nand->priv; struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d; 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; 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 nand_chip *nand = mtd->priv;
struct mxs_nand_info *nand_info = nand->priv; struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d; 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; int ret;
if (length > NAND_MAX_PAGESIZE) { 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 nand_chip *nand = mtd->priv;
struct mxs_nand_info *nand_info = nand->priv; struct mxs_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d; 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; int ret;
if (length > NAND_MAX_PAGESIZE) { 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_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d; 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; uint32_t corrected = 0, failed = 0;
uint8_t *status; uint8_t *status;
int i, ret; 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_nand_info *nand_info = nand->priv;
struct mxs_dma_desc *d; 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; int ret = 0;
memcpy(nand_info->data_buf, buf, mtd->writesize); 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 nand_chip *nand = mtd->priv;
struct mxs_nand_info *nand_info = nand->priv; struct mxs_nand_info *nand_info = nand->priv;
void __iomem *bch_regs = nand_info->bch_base; void __iomem *bch_regs = nand_info->bch_base;
uint32_t tmp; uint32_t layout0, layout1;
int ret; int ret;
/* Reset BCH. Don't use SFTRST on MX23 due to Errata #2847 */ /* 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) if (ret)
return ret; return ret;
/* Configure layout 0 */ if (mxs_nand_is_imx6(nand_info)) {
tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) layout0 = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1)
<< BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET |
tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET |
tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
<< BCH_FLASHLAYOUT0_ECC0_OFFSET; << IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET |
tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2;
writel(tmp, bch_regs + BCH_FLASH0LAYOUT0);
tmp = (mtd->writesize + mtd->oobsize) layout1 = (mtd->writesize + mtd->oobsize)
<< BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET |
tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1)
<< BCH_FLASHLAYOUT1_ECCN_OFFSET; << IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET |
tmp |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; MXS_NAND_CHUNK_DATA_CHUNK_SIZE >> 2;
writel(tmp, bch_regs + BCH_FLASH0LAYOUT1); } 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 */ /* Set *all* chip selects to use layout 0 */
writel(0, bch_regs + BCH_LAYOUTSELECT); 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 mxs_nand_info *nand_info;
struct nand_chip *nand; struct nand_chip *nand;
struct mtd_info *mtd; struct mtd_info *mtd;
enum gpmi_type type;
int err; 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); nand_info = kzalloc(sizeof(struct mxs_nand_info), GFP_KERNEL);
if (!nand_info) { if (!nand_info) {
printf("MXS NAND: Failed to allocate private data\n"); printf("MXS NAND: Failed to allocate private data\n");
return -ENOMEM; return -ENOMEM;
} }
nand_info->type = type;
nand_info->io_base = dev_request_mem_region(dev, 0); nand_info->io_base = dev_request_mem_region(dev, 0);
nand_info->bch_base = dev_request_mem_region(dev, 1); 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)) if (IS_ERR(nand_info->clk))
return PTR_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); err = mxs_nand_alloc_buffers(nand_info);
if (err) if (err)
@ -1268,9 +1310,25 @@ err1:
return err; 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 = { static struct driver_d mxs_nand_driver = {
.name = "mxs_nand", .name = "mxs_nand",
.probe = mxs_nand_probe, .probe = mxs_nand_probe,
.of_compatible = DRV_OF_COMPAT(gpmi_dt_ids),
}; };
device_platform_driver(mxs_nand_driver); device_platform_driver(mxs_nand_driver);