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[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);

View File

@ -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

View File

@ -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!

View File

@ -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

View File

@ -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);
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;
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);
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);