Commit ce4c61f1 authored by Thomas Gleixner's avatar Thomas Gleixner

[MTD] Add support for NDFC NAND controller

NDFC NAND Flash controller is embedded in PPC EP44x SoCs.
Add platform driver based support.
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 41796c2e
...@@ -129,6 +129,12 @@ config MTD_NAND_S3C2410_HWECC ...@@ -129,6 +129,12 @@ config MTD_NAND_S3C2410_HWECC
currently not be able to switch to software, as there is no currently not be able to switch to software, as there is no
implementation for ECC method used by the S3C2410 implementation for ECC method used by the S3C2410
config MTD_NAND_NDFC
tristate "NDFC NanD Flash Controller"
depends on MTD_NAND && 44x
help
NDFC Nand Flash Controllers are integrated in EP44x SoCs
config MTD_NAND_DISKONCHIP config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
depends on MTD_NAND && EXPERIMENTAL depends on MTD_NAND && EXPERIMENTAL
......
...@@ -21,5 +21,6 @@ obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o ...@@ -21,5 +21,6 @@ obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
nand-objs = nand_base.o nand_bbt.o nand-objs = nand_base.o nand_bbt.o
/*
* drivers/mtd/ndfc.c
*
* Overview:
* Platform independend driver for NDFC (NanD Flash Controller)
* integrated into EP440 cores
*
* Author: Thomas Gleixner
*
* Copyright 2006 IBM
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/ndfc.h>
#include <linux/mtd/ubi.h>
#include <linux/mtd/mtd.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/ibm44x.h>
struct ndfc_nand_mtd {
struct mtd_info mtd;
struct nand_chip chip;
struct platform_nand_chip *pl_chip;
};
static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS];
struct ndfc_controller {
void __iomem *ndfcbase;
struct nand_hw_control ndfc_control;
atomic_t childs_active;
};
static struct ndfc_controller ndfc_ctrl;
static void ndfc_select_chip(struct mtd_info *mtd, int chip)
{
uint32_t ccr;
struct ndfc_controller *ndfc = &ndfc_ctrl;
struct nand_chip *nandchip = mtd->priv;
struct ndfc_nand_mtd *nandmtd = nandchip->priv;
struct platform_nand_chip *pchip = nandmtd->pl_chip;
ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
if (chip >= 0) {
ccr &= ~NDFC_CCR_BS_MASK;
ccr |= NDFC_CCR_BS(chip + pchip->chip_offset);
} else
ccr |= NDFC_CCR_RESET_CE;
writel(ccr, ndfc->ndfcbase + NDFC_CCR);
}
static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
struct nand_chip *chip = mtd->priv;
switch (cmd) {
case NAND_CTL_SETCLE:
chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_CMD;
break;
case NAND_CTL_SETALE:
chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_ALE;
break;
default:
chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
break;
}
}
static int ndfc_ready(struct mtd_info *mtd)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
}
static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
{
uint32_t ccr;
struct ndfc_controller *ndfc = &ndfc_ctrl;
ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
ccr |= NDFC_CCR_RESET_ECC;
__raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR);
wmb();
}
static int ndfc_calculate_ecc(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_code)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
uint32_t ecc;
uint8_t *p = (uint8_t *)&ecc;
wmb();
ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC);
ecc_code[0] = p[1];
ecc_code[1] = p[2];
ecc_code[2] = p[3];
return 0;
}
/*
* Speedups for buffer read/write/verify
*
* NDFC allows 32bit read/write of data. So we can speed up the buffer
* functions. No further checking, as nand_base will always read/write
* page aligned.
*/
static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
uint32_t *p = (uint32_t *) buf;
for(;len > 0; len -= 4)
*p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA);
}
static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
uint32_t *p = (uint32_t *) buf;
for(;len > 0; len -= 4)
__raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA);
}
static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
uint32_t *p = (uint32_t *) buf;
for(;len > 0; len -= 4)
if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA))
return -EFAULT;
return 0;
}
/*
* Initialize chip structure
*/
static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
{
struct ndfc_controller *ndfc = &ndfc_ctrl;
struct nand_chip *chip = &mtd->chip;
chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
chip->hwcontrol = ndfc_hwcontrol;
chip->dev_ready = ndfc_ready;
chip->select_chip = ndfc_select_chip;
chip->chip_delay = 50;
chip->priv = mtd;
chip->options = mtd->pl_chip->options;
chip->controller = &ndfc->ndfc_control;
chip->read_buf = ndfc_read_buf;
chip->write_buf = ndfc_write_buf;
chip->verify_buf = ndfc_verify_buf;
chip->correct_data = nand_correct_data;
chip->enable_hwecc = ndfc_enable_hwecc;
chip->calculate_ecc = ndfc_calculate_ecc;
chip->eccmode = NAND_ECC_HW3_256;
chip->autooob = mtd->pl_chip->autooob;
mtd->mtd.priv = chip;
mtd->mtd.owner = THIS_MODULE;
}
static int ndfc_chip_probe(struct platform_device *pdev)
{
int rc;
struct platform_nand_chip *nc = pdev->dev.platform_data;
struct ndfc_chip_settings *settings = nc->priv;
struct ndfc_controller *ndfc = &ndfc_ctrl;
struct ndfc_nand_mtd *nandmtd;
if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS)
return -EINVAL;
/* Set the bank settings */
__raw_writel(settings->bank_settings,
ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2));
nandmtd = &ndfc_mtd[pdev->id];
if (nandmtd->pl_chip)
return -EBUSY;
nandmtd->pl_chip = nc;
ndfc_chip_init(nandmtd);
/* Scan for chips */
if (nand_scan(&nandmtd->mtd, nc->nr_chips)) {
nandmtd->pl_chip = NULL;
return -ENODEV;
}
#ifdef CONFIG_MTD_PARTITIONS
printk("Number of partitions %d\n", nc->nr_partitions);
if (nc->nr_partitions) {
struct mtd_info *mtd_ubi;
nc->partitions[NAND_PARTS_CONTENT_IDX].mtdp = &mtd_ubi;
add_mtd_device(&nandmtd->mtd); /* for testing */
add_mtd_partitions(&nandmtd->mtd,
nc->partitions,
nc->nr_partitions);
add_mtd_device(mtd_ubi);
} else
#else
add_mtd_device(&nandmtd->mtd);
#endif
atomic_inc(&ndfc->childs_active);
return 0;
}
static int ndfc_chip_remove(struct platform_device *pdev)
{
return 0;
}
static int ndfc_nand_probe(struct platform_device *pdev)
{
struct platform_nand_ctrl *nc = pdev->dev.platform_data;
struct ndfc_controller_settings *settings = nc->priv;
struct resource *res = pdev->resource;
struct ndfc_controller *ndfc = &ndfc_ctrl;
unsigned long long phys = NDFC_PHYSADDR_OFFS | res->start;
ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
if (!ndfc->ndfcbase) {
printk(KERN_ERR "NDFC: ioremap failed\n");
return -EIO;
}
__raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR);
spin_lock_init(&ndfc->ndfc_control.lock);
init_waitqueue_head(&ndfc->ndfc_control.wq);
platform_set_drvdata(pdev, ndfc);
printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n",
__raw_readl(ndfc->ndfcbase + NDFC_REVID));
return 0;
}
static int ndfc_nand_remove(struct platform_device *pdev)
{
struct ndfc_controller *ndfc = platform_get_drvdata(pdev);
if (atomic_read(&ndfc->childs_active))
return -EBUSY;
if (ndfc) {
platform_set_drvdata(pdev, NULL);
iounmap(ndfc_ctrl.ndfcbase);
ndfc_ctrl.ndfcbase = NULL;
}
return 0;
}
/* driver device registration */
static struct platform_driver ndfc_chip_driver = {
.probe = ndfc_chip_probe,
.remove = ndfc_chip_remove,
.driver = {
.name = "ndfc-chip",
.owner = THIS_MODULE,
},
};
static struct platform_driver ndfc_nand_driver = {
.probe = ndfc_nand_probe,
.remove = ndfc_nand_remove,
.driver = {
.name = "ndfc-nand",
.owner = THIS_MODULE,
},
};
static int __init ndfc_nand_init(void)
{
int ret = platform_driver_register(&ndfc_nand_driver);
if (!ret)
ret = platform_driver_register(&ndfc_chip_driver);
return ret;
}
static void __exit ndfc_nand_exit(void)
{
platform_driver_unregister(&ndfc_chip_driver);
platform_driver_unregister(&ndfc_nand_driver);
}
module_init(ndfc_nand_init);
module_exit(ndfc_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
MODULE_DESCRIPTION("Platform driver for NDFC");
/*
* linux/include/linux/mtd/ndfc.h
*
* Copyright (c) 2006 Thomas Gleixner <tglx@linutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Info:
* Contains defines, datastructures for ndfc nand controller
*
*/
#ifndef __LINUX_MTD_NDFC_H
#define __LINUX_MTD_NDFC_H
/* NDFC Register definitions */
#define NDFC_CMD 0x00
#define NDFC_ALE 0x04
#define NDFC_DATA 0x08
#define NDFC_ECC 0x10
#define NDFC_BCFG0 0x30
#define NDFC_BCFG1 0x34
#define NDFC_BCFG2 0x38
#define NDFC_BCFG3 0x3c
#define NDFC_CCR 0x40
#define NDFC_STAT 0x44
#define NDFC_HWCTL 0x48
#define NDFC_REVID 0x50
#define NDFC_STAT_IS_READY 0x01000000
#define NDFC_CCR_RESET_CE 0x80000000 /* CE Reset */
#define NDFC_CCR_RESET_ECC 0x40000000 /* ECC Reset */
#define NDFC_CCR_RIE 0x20000000 /* Interrupt Enable on Device Rdy */
#define NDFC_CCR_REN 0x10000000 /* Enable wait for Rdy in LinearR */
#define NDFC_CCR_ROMEN 0x08000000 /* Enable ROM In LinearR */
#define NDFC_CCR_ARE 0x04000000 /* Auto-Read Enable */
#define NDFC_CCR_BS(x) (((x) & 0x3) << 24) /* Select Bank on CE[x] */
#define NDFC_CCR_BS_MASK 0x03000000 /* Select Bank */
#define NDFC_CCR_ARAC0 0x00000000 /* 3 Addr, 1 Col 2 Row 512b page */
#define NDFC_CCR_ARAC1 0x00001000 /* 4 Addr, 1 Col 3 Row 512b page */
#define NDFC_CCR_ARAC2 0x00002000 /* 4 Addr, 2 Col 2 Row 2K page */
#define NDFC_CCR_ARAC3 0x00003000 /* 5 Addr, 2 Col 3 Row 2K page */
#define NDFC_CCR_ARAC_MASK 0x00003000 /* Auto-Read mode Addr Cycles */
#define NDFC_CCR_RPG 0x0000C000 /* Auto-Read Page */
#define NDFC_CCR_EBCC 0x00000004 /* EBC Configuration Completed */
#define NDFC_CCR_DHC 0x00000002 /* Direct Hardware Control Enable */
#define NDFC_BxCFG_EN 0x80000000 /* Bank Enable */
#define NDFC_BxCFG_CED 0x40000000 /* nCE Style */
#define NDFC_BxCFG_SZ_MASK 0x08000000 /* Bank Size */
#define NDFC_BxCFG_SZ_8BIT 0x00000000 /* 8bit */
#define NDFC_BxCFG_SZ_16BIT 0x08000000 /* 16bit */
#define NDFC_MAX_BANKS 4
struct ndfc_controller_settings {
uint32_t ccr_settings;
};
struct ndfc_chip_settings {
uint32_t bank_settings;
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment