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:
parent
a4f58f5665
commit
0f15dee78f
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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!
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue