linux-stable/drivers/mtd/nand/bf5xx_nand.c
Boris Brezillon d4092d76a4 mtd: nand: Rename nand.h into rawnand.h
We are planning to share more code between different NAND based
devices (SPI NAND, OneNAND and raw NANDs), but before doing that
we need to move the existing include/linux/mtd/nand.h file into
include/linux/mtd/rawnand.h so we can later create a nand.h header
containing all common structure and function prototypes.

Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Signed-off-by: Peter Pan <peterpandong@micron.com>
Acked-by: Vladimir Zapolskiy <vz@mleia.com>
Acked-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
Acked-by: Wenyou Yang <wenyou.yang@microchip.com>
Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
Acked-by: Han Xu <han.xu@nxp.com>
Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Acked-by: Shawn Guo <shawnguo@kernel.org>
Acked-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Acked-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Masahiro Yamada <yamada.masahiro@socionext.com>
Acked-By: Harvey Hunt <harveyhuntnexus@gmail.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Acked-by: Krzysztof Halasa <khalasa@piap.pl>
2017-08-13 10:11:49 +02:00

860 lines
20 KiB
C

/* linux/drivers/mtd/nand/bf5xx_nand.c
*
* Copyright 2006-2008 Analog Devices Inc.
* http://blackfin.uclinux.org/
* Bryan Wu <bryan.wu@analog.com>
*
* Blackfin BF5xx on-chip NAND flash controller driver
*
* Derived from drivers/mtd/nand/s3c2410.c
* Copyright (c) 2007 Ben Dooks <ben@simtec.co.uk>
*
* Derived from drivers/mtd/nand/cafe.c
* Copyright © 2006 Red Hat, Inc.
* Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
*
* Changelog:
* 12-Jun-2007 Bryan Wu: Initial version
* 18-Jul-2007 Bryan Wu:
* - ECC_HW and ECC_SW supported
* - DMA supported in ECC_HW
* - YAFFS tested as rootfs in both ECC_HW and ECC_SW
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/blackfin.h>
#include <asm/dma.h>
#include <asm/cacheflush.h>
#include <asm/nand.h>
#include <asm/portmux.h>
#define DRV_NAME "bf5xx-nand"
#define DRV_VERSION "1.2"
#define DRV_AUTHOR "Bryan Wu <bryan.wu@analog.com>"
#define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver"
/* NFC_STAT Masks */
#define NBUSY 0x01 /* Not Busy */
#define WB_FULL 0x02 /* Write Buffer Full */
#define PG_WR_STAT 0x04 /* Page Write Pending */
#define PG_RD_STAT 0x08 /* Page Read Pending */
#define WB_EMPTY 0x10 /* Write Buffer Empty */
/* NFC_IRQSTAT Masks */
#define NBUSYIRQ 0x01 /* Not Busy IRQ */
#define WB_OVF 0x02 /* Write Buffer Overflow */
#define WB_EDGE 0x04 /* Write Buffer Edge Detect */
#define RD_RDY 0x08 /* Read Data Ready */
#define WR_DONE 0x10 /* Page Write Done */
/* NFC_RST Masks */
#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */
/* NFC_PGCTL Masks */
#define PG_RD_START 0x01 /* Page Read Start */
#define PG_WR_START 0x02 /* Page Write Start */
#ifdef CONFIG_MTD_NAND_BF5XX_HWECC
static int hardware_ecc = 1;
#else
static int hardware_ecc;
#endif
static const unsigned short bfin_nfc_pin_req[] =
{P_NAND_CE,
P_NAND_RB,
P_NAND_D0,
P_NAND_D1,
P_NAND_D2,
P_NAND_D3,
P_NAND_D4,
P_NAND_D5,
P_NAND_D6,
P_NAND_D7,
P_NAND_WE,
P_NAND_RE,
P_NAND_CLE,
P_NAND_ALE,
0};
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
static int bootrom_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 7)
return -ERANGE;
oobregion->offset = section * 8;
oobregion->length = 3;
return 0;
}
static int bootrom_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
if (section > 7)
return -ERANGE;
oobregion->offset = (section * 8) + 3;
oobregion->length = 5;
return 0;
}
static const struct mtd_ooblayout_ops bootrom_ooblayout_ops = {
.ecc = bootrom_ooblayout_ecc,
.free = bootrom_ooblayout_free,
};
#endif
/*
* Data structures for bf5xx nand flash controller driver
*/
/* bf5xx nand info */
struct bf5xx_nand_info {
/* mtd info */
struct nand_hw_control controller;
struct nand_chip chip;
/* platform info */
struct bf5xx_nand_platform *platform;
/* device info */
struct device *device;
/* DMA stuff */
struct completion dma_completion;
};
/*
* Conversion functions
*/
static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd)
{
return container_of(mtd_to_nand(mtd), struct bf5xx_nand_info,
chip);
}
static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
{
return platform_get_drvdata(pdev);
}
static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
{
return dev_get_platdata(&pdev->dev);
}
/*
* struct nand_chip interface function pointers
*/
/*
* bf5xx_nand_hwcontrol
*
* Issue command and address cycles to the chip
*/
static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
if (cmd == NAND_CMD_NONE)
return;
while (bfin_read_NFC_STAT() & WB_FULL)
cpu_relax();
if (ctrl & NAND_CLE)
bfin_write_NFC_CMD(cmd);
else if (ctrl & NAND_ALE)
bfin_write_NFC_ADDR(cmd);
SSYNC();
}
/*
* bf5xx_nand_devready()
*
* returns 0 if the nand is busy, 1 if it is ready
*/
static int bf5xx_nand_devready(struct mtd_info *mtd)
{
unsigned short val = bfin_read_NFC_STAT();
if ((val & NBUSY) == NBUSY)
return 1;
else
return 0;
}
/*
* ECC functions
* These allow the bf5xx to use the controller's ECC
* generator block to ECC the data as it passes through
*/
/*
* ECC error correction function
*/
static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
u32 syndrome[5];
u32 calced, stored;
int i;
unsigned short failing_bit, failing_byte;
u_char data;
calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);
stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);
syndrome[0] = (calced ^ stored);
/*
* syndrome 0: all zero
* No error in data
* No action
*/
if (!syndrome[0] || !calced || !stored)
return 0;
/*
* sysdrome 0: only one bit is one
* ECC data was incorrect
* No action
*/
if (hweight32(syndrome[0]) == 1) {
dev_err(info->device, "ECC data was incorrect!\n");
return -EBADMSG;
}
syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);
syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);
syndrome[4] = syndrome[2] ^ syndrome[3];
for (i = 0; i < 5; i++)
dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]);
dev_info(info->device,
"calced[0x%08x], stored[0x%08x]\n",
calced, stored);
/*
* sysdrome 0: exactly 11 bits are one, each parity
* and parity' pair is 1 & 0 or 0 & 1.
* 1-bit correctable error
* Correct the error
*/
if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {
dev_info(info->device,
"1-bit correctable error, correct it.\n");
dev_info(info->device,
"syndrome[1] 0x%08x\n", syndrome[1]);
failing_bit = syndrome[1] & 0x7;
failing_byte = syndrome[1] >> 0x3;
data = *(dat + failing_byte);
data = data ^ (0x1 << failing_bit);
*(dat + failing_byte) = data;
return 1;
}
/*
* sysdrome 0: random data
* More than 1-bit error, non-correctable error
* Discard data, mark bad block
*/
dev_err(info->device,
"More than 1-bit error, non-correctable error.\n");
dev_err(info->device,
"Please discard data, mark bad block\n");
return -EBADMSG;
}
static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
struct nand_chip *chip = mtd_to_nand(mtd);
int ret, bitflips = 0;
ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
if (ret < 0)
return ret;
bitflips = ret;
/* If ecc size is 512, correct second 256 bytes */
if (chip->ecc.size == 512) {
dat += 256;
read_ecc += 3;
calc_ecc += 3;
ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
if (ret < 0)
return ret;
bitflips += ret;
}
return bitflips;
}
static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
return;
}
static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_code)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
struct nand_chip *chip = mtd_to_nand(mtd);
u16 ecc0, ecc1;
u32 code[2];
u8 *p;
/* first 3 bytes ECC code for 256 page size */
ecc0 = bfin_read_NFC_ECC0();
ecc1 = bfin_read_NFC_ECC1();
code[0] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);
dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]);
p = (u8 *) code;
memcpy(ecc_code, p, 3);
/* second 3 bytes ECC code for 512 ecc size */
if (chip->ecc.size == 512) {
ecc0 = bfin_read_NFC_ECC2();
ecc1 = bfin_read_NFC_ECC3();
code[1] = (ecc0 & 0x7ff) | ((ecc1 & 0x7ff) << 11);
/* second 3 bytes in ecc_code for second 256
* bytes of 512 page size
*/
p = (u8 *) (code + 1);
memcpy((ecc_code + 3), p, 3);
dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]);
}
return 0;
}
/*
* PIO mode for buffer writing and reading
*/
static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
unsigned short val;
/*
* Data reads are requested by first writing to NFC_DATA_RD
* and then reading back from NFC_READ.
*/
for (i = 0; i < len; i++) {
while (bfin_read_NFC_STAT() & WB_FULL)
cpu_relax();
/* Contents do not matter */
bfin_write_NFC_DATA_RD(0x0000);
SSYNC();
while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY)
cpu_relax();
buf[i] = bfin_read_NFC_READ();
val = bfin_read_NFC_IRQSTAT();
val |= RD_RDY;
bfin_write_NFC_IRQSTAT(val);
SSYNC();
}
}
static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd)
{
uint8_t val;
bf5xx_nand_read_buf(mtd, &val, 1);
return val;
}
static void bf5xx_nand_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
int i;
for (i = 0; i < len; i++) {
while (bfin_read_NFC_STAT() & WB_FULL)
cpu_relax();
bfin_write_NFC_DATA_WR(buf[i]);
SSYNC();
}
}
static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
u16 *p = (u16 *) buf;
len >>= 1;
/*
* Data reads are requested by first writing to NFC_DATA_RD
* and then reading back from NFC_READ.
*/
bfin_write_NFC_DATA_RD(0x5555);
SSYNC();
for (i = 0; i < len; i++)
p[i] = bfin_read_NFC_READ();
}
static void bf5xx_nand_write_buf16(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
int i;
u16 *p = (u16 *) buf;
len >>= 1;
for (i = 0; i < len; i++)
bfin_write_NFC_DATA_WR(p[i]);
SSYNC();
}
/*
* DMA functions for buffer writing and reading
*/
static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
{
struct bf5xx_nand_info *info = dev_id;
clear_dma_irqstat(CH_NFC);
disable_dma(CH_NFC);
complete(&info->dma_completion);
return IRQ_HANDLED;
}
static void bf5xx_nand_dma_rw(struct mtd_info *mtd,
uint8_t *buf, int is_read)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
struct nand_chip *chip = mtd_to_nand(mtd);
unsigned short val;
dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n",
mtd, buf, is_read);
/*
* Before starting a dma transfer, be sure to invalidate/flush
* the cache over the address range of your DMA buffer to
* prevent cache coherency problems. Otherwise very subtle bugs
* can be introduced to your driver.
*/
if (is_read)
invalidate_dcache_range((unsigned int)buf,
(unsigned int)(buf + chip->ecc.size));
else
flush_dcache_range((unsigned int)buf,
(unsigned int)(buf + chip->ecc.size));
/*
* This register must be written before each page is
* transferred to generate the correct ECC register
* values.
*/
bfin_write_NFC_RST(ECC_RST);
SSYNC();
while (bfin_read_NFC_RST() & ECC_RST)
cpu_relax();
disable_dma(CH_NFC);
clear_dma_irqstat(CH_NFC);
/* setup DMA register with Blackfin DMA API */
set_dma_config(CH_NFC, 0x0);
set_dma_start_addr(CH_NFC, (unsigned long) buf);
/* The DMAs have different size on BF52x and BF54x */
#ifdef CONFIG_BF52x
set_dma_x_count(CH_NFC, (chip->ecc.size >> 1));
set_dma_x_modify(CH_NFC, 2);
val = DI_EN | WDSIZE_16;
#endif
#ifdef CONFIG_BF54x
set_dma_x_count(CH_NFC, (chip->ecc.size >> 2));
set_dma_x_modify(CH_NFC, 4);
val = DI_EN | WDSIZE_32;
#endif
/* setup write or read operation */
if (is_read)
val |= WNR;
set_dma_config(CH_NFC, val);
enable_dma(CH_NFC);
/* Start PAGE read/write operation */
if (is_read)
bfin_write_NFC_PGCTL(PG_RD_START);
else
bfin_write_NFC_PGCTL(PG_WR_START);
wait_for_completion(&info->dma_completion);
}
static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
uint8_t *buf, int len)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
struct nand_chip *chip = mtd_to_nand(mtd);
dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len);
if (len == chip->ecc.size)
bf5xx_nand_dma_rw(mtd, buf, 1);
else
bf5xx_nand_read_buf(mtd, buf, len);
}
static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
struct nand_chip *chip = mtd_to_nand(mtd);
dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len);
if (len == chip->ecc.size)
bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0);
else
bf5xx_nand_write_buf(mtd, buf, len);
}
static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page)
{
bf5xx_nand_read_buf(mtd, buf, mtd->writesize);
bf5xx_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
static int bf5xx_nand_write_page_raw(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required,
int page)
{
bf5xx_nand_write_buf(mtd, buf, mtd->writesize);
bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0;
}
/*
* System initialization functions
*/
static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
{
int ret;
/* Do not use dma */
if (!hardware_ecc)
return 0;
init_completion(&info->dma_completion);
/* Request NFC DMA channel */
ret = request_dma(CH_NFC, "BF5XX NFC driver");
if (ret < 0) {
dev_err(info->device, " unable to get DMA channel\n");
return ret;
}
#ifdef CONFIG_BF54x
/* Setup DMAC1 channel mux for NFC which shared with SDH */
bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1);
SSYNC();
#endif
set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info);
/* Turn off the DMA channel first */
disable_dma(CH_NFC);
return 0;
}
static void bf5xx_nand_dma_remove(struct bf5xx_nand_info *info)
{
/* Free NFC DMA channel */
if (hardware_ecc)
free_dma(CH_NFC);
}
/*
* BF5XX NFC hardware initialization
* - pin mux setup
* - clear interrupt status
*/
static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
{
int err = 0;
unsigned short val;
struct bf5xx_nand_platform *plat = info->platform;
/* setup NFC_CTL register */
dev_info(info->device,
"data_width=%d, wr_dly=%d, rd_dly=%d\n",
(plat->data_width ? 16 : 8),
plat->wr_dly, plat->rd_dly);
val = (1 << NFC_PG_SIZE_OFFSET) |
(plat->data_width << NFC_NWIDTH_OFFSET) |
(plat->rd_dly << NFC_RDDLY_OFFSET) |
(plat->wr_dly << NFC_WRDLY_OFFSET);
dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val);
bfin_write_NFC_CTL(val);
SSYNC();
/* clear interrupt status */
bfin_write_NFC_IRQMASK(0x0);
SSYNC();
val = bfin_read_NFC_IRQSTAT();
bfin_write_NFC_IRQSTAT(val);
SSYNC();
/* DMA initialization */
if (bf5xx_nand_dma_init(info))
err = -ENXIO;
return err;
}
/*
* Device management interface
*/
static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
{
struct mtd_info *mtd = nand_to_mtd(&info->chip);
struct mtd_partition *parts = info->platform->partitions;
int nr = info->platform->nr_partitions;
return mtd_device_register(mtd, parts, nr);
}
static int bf5xx_nand_remove(struct platform_device *pdev)
{
struct bf5xx_nand_info *info = to_nand_info(pdev);
/* first thing we need to do is release all our mtds
* and their partitions, then go through freeing the
* resources used
*/
nand_release(nand_to_mtd(&info->chip));
peripheral_free_list(bfin_nfc_pin_req);
bf5xx_nand_dma_remove(info);
return 0;
}
static int bf5xx_nand_scan(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd_to_nand(mtd);
int ret;
ret = nand_scan_ident(mtd, 1, NULL);
if (ret)
return ret;
if (hardware_ecc) {
/*
* for nand with page size > 512B, think it as several sections with 512B
*/
if (likely(mtd->writesize >= 512)) {
chip->ecc.size = 512;
chip->ecc.bytes = 6;
chip->ecc.strength = 2;
} else {
chip->ecc.size = 256;
chip->ecc.bytes = 3;
chip->ecc.strength = 1;
bfin_write_NFC_CTL(bfin_read_NFC_CTL() & ~(1 << NFC_PG_SIZE_OFFSET));
SSYNC();
}
}
return nand_scan_tail(mtd);
}
/*
* bf5xx_nand_probe
*
* called by device layer when it finds a device matching
* one our driver can handled. This code checks to see if
* it can allocate all necessary resources then calls the
* nand layer to look for devices
*/
static int bf5xx_nand_probe(struct platform_device *pdev)
{
struct bf5xx_nand_platform *plat = to_nand_plat(pdev);
struct bf5xx_nand_info *info = NULL;
struct nand_chip *chip = NULL;
struct mtd_info *mtd = NULL;
int err = 0;
dev_dbg(&pdev->dev, "(%p)\n", pdev);
if (!plat) {
dev_err(&pdev->dev, "no platform specific information\n");
return -EINVAL;
}
if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) {
dev_err(&pdev->dev, "requesting Peripherals failed\n");
return -EFAULT;
}
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (info == NULL) {
err = -ENOMEM;
goto out_err;
}
platform_set_drvdata(pdev, info);
nand_hw_control_init(&info->controller);
info->device = &pdev->dev;
info->platform = plat;
/* initialise chip data struct */
chip = &info->chip;
mtd = nand_to_mtd(&info->chip);
if (plat->data_width)
chip->options |= NAND_BUSWIDTH_16;
chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN;
chip->read_buf = (plat->data_width) ?
bf5xx_nand_read_buf16 : bf5xx_nand_read_buf;
chip->write_buf = (plat->data_width) ?
bf5xx_nand_write_buf16 : bf5xx_nand_write_buf;
chip->read_byte = bf5xx_nand_read_byte;
chip->cmd_ctrl = bf5xx_nand_hwcontrol;
chip->dev_ready = bf5xx_nand_devready;
nand_set_controller_data(chip, mtd);
chip->controller = &info->controller;
chip->IO_ADDR_R = (void __iomem *) NFC_READ;
chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR;
chip->chip_delay = 0;
/* initialise mtd info data struct */
mtd->dev.parent = &pdev->dev;
/* initialise the hardware */
err = bf5xx_nand_hw_init(info);
if (err)
goto out_err;
/* setup hardware ECC data struct */
if (hardware_ecc) {
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
mtd_set_ooblayout(mtd, &bootrom_ooblayout_ops);
#endif
chip->read_buf = bf5xx_nand_dma_read_buf;
chip->write_buf = bf5xx_nand_dma_write_buf;
chip->ecc.calculate = bf5xx_nand_calculate_ecc;
chip->ecc.correct = bf5xx_nand_correct_data;
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.hwctl = bf5xx_nand_enable_hwecc;
chip->ecc.read_page_raw = bf5xx_nand_read_page_raw;
chip->ecc.write_page_raw = bf5xx_nand_write_page_raw;
} else {
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
}
/* scan hardware nand chip and setup mtd info data struct */
if (bf5xx_nand_scan(mtd)) {
err = -ENXIO;
goto out_err_nand_scan;
}
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
chip->badblockpos = 63;
#endif
/* add NAND partition */
bf5xx_nand_add_partition(info);
dev_dbg(&pdev->dev, "initialised ok\n");
return 0;
out_err_nand_scan:
bf5xx_nand_dma_remove(info);
out_err:
peripheral_free_list(bfin_nfc_pin_req);
return err;
}
/* driver device registration */
static struct platform_driver bf5xx_nand_driver = {
.probe = bf5xx_nand_probe,
.remove = bf5xx_nand_remove,
.driver = {
.name = DRV_NAME,
},
};
module_platform_driver(bf5xx_nand_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_ALIAS("platform:" DRV_NAME);