这是sta2065 平台的 nandflash 驱动实现代码,位于drivers/mtd/nand/cartesio_nand.c
1. init/exit:
static int __init cartesio_nand_init(void)
{
return platform_driver_register(&cartesio_nand_driver);
}
static void __exit cartesio_nand_exit(void)
{
platform_driver_unregister(&cartesio_nand_driver);
}
module_init(cartesio_nand_init);
module_exit(cartesio_nand_exit);
static struct platform_driver cartesio_nand_driver = {
.probe = cartesio_nand_probe,
.remove = __devexit_p(cartesio_nand_remove),
.driver = {
.name = "CARTESIO-NAND",
.owner = THIS_MODULE,
},
};
2. static int __devinit cartesio_nand_probe(struct platform_device *pdev)
在cartesio_nand_probe 函数中会赋值一堆函数指针,这些函数都会在这个代码中实现。
/*
* Probe for the NAND device
*/
static int __devinit cartesio_nand_probe(struct platform_device *pdev)
{
struct platform_nand_data *pdata = pdev->dev.platform_data;
struct cartesio_nand_data *data;
struct resource *res;
#if defined(CONFIG_MACH_CARTESIO_CHAPLIN) || defined (CONFIG_MACH_CARTESIO_TIGER4)
char buf[4];
int status;
#endif
#if defined(CONFIG_MACH_CARTESIO_CHAPLIN) || defined (CONFIG_MACH_CARTESIO_TIGER4) || defined(CONFIG_CARTESIO_NAND_DMA)
struct nand_chip *chip;
struct mtd_info *mtd;
#endif
int ret = 0;
#ifdef CONFIG_CARTESIO_NAND_DMA
struct pl080_dma_slave *client;
dma_cap_mask_t mask;
#endif
/* must have platform data */
if (!pdata) {
dev_err(&pdev->dev, "missing platform data\n");
return -ENODEV;
}
if (pdata->chip.chip_offset >= FSMC_DEVICE_NUM || pdata->chip.nr_chips != 1)
return -EINVAL;
/* must have reference to parent FSMC device */
if (!pdev->dev.parent) {
dev_err(&pdev->dev, "missing reference to parent FSMC device\n");
return -ENODEV;
}
/* allocate memory for the device structure (and zero it) */
data = kzalloc(sizeof(struct cartesio_nand_data), GFP_KERNEL);
if (!data) {
dev_err(&pdev->dev, "failed to allocate device structure\n");
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "missing I/O memory resource\n");
kfree(data);
return -ENXIO;
}
data->io_base = ioremap(res->start, resource_size(res));
if (data->io_base == NULL) {
dev_err(&pdev->dev, "failed to remap memory space\n");
kfree(data);
return -EIO;
}
data->chip.priv = data;
data->mtd.priv = &data->chip;
data->mtd.owner = THIS_MODULE;
data->mtd.name = dev_name(&pdev->dev);
data->chip.IO_ADDR_R = data->io_base + FSMC_NAND_DIO;
data->chip.IO_ADDR_W = data->io_base + FSMC_NAND_DIO;
data->chip.cmd_ctrl = cartesio_nand_hwcontrol; //赋值一堆函数指针,这些都是nand_chip设备的操作函数。
data->chip.dev_ready = pdata->ctrl.dev_ready;
data->chip.select_chip = pdata->ctrl.select_chip;
data->chip.write_buf = pdata->ctrl.write_buf;
data->chip.read_buf = pdata->ctrl.read_buf;
data->chip.chip_delay = pdata->chip.chip_delay;
data->chip.options |= pdata->chip.options;
data->chip.ecc.mode = NAND_ECC_HW;
#ifdef CONFIG_BCH_8
data->chip.ecc.bytes = 13;
data->chip.ecc.read_page = cartesio_nand_read_page_hwecc;
#ifdef CONFIG_CARTESIO_NAND_DMA
data->chip.ecc.write_page = cartesio_nand_write_page_hwecc;
#endif
data->chip.ecc.correct = cartesio_nand_correctdata;
data->chip.read_buf = cartesio_nand_read_buf;
#else
data->chip.ecc.bytes = 3;
data->chip.ecc.correct = nand_correct_data;
#endif
data->chip.ecclayout = pdata->chip.ecclayout;
data->chip.ecc.layout = pdata->chip.ecclayout;
data->chip.ecc.hwctl = cartesio_nand_hwctl;
data->chip.ecc.calculate = cartesio_nand_calculate;
data->chip.bbt_td = &cartesio_nand_bbt_descr;
data->chip.bbt_md = &cartesio_nand_bbt_descr;
data->fsmc_device = pdev->dev.parent;
data->fsmc_slot = pdata->chip.chip_offset;
platform_set_drvdata(pdev, data);
/* handle any platform specific setup */
if (pdata->ctrl.probe) {
ret = pdata->ctrl.probe(pdev);
if (ret)
goto out;
}
/* first scan to find the device and get the page size */
if (nand_scan_ident(&data->mtd, pdata->chip.nr_chips)) {
ret = -ENXIO;
goto out;
}
/* set ECC page size and oob layout */
switch (data->mtd.writesize) {
case 256:
data->chip.ecc.steps = 1;
data->chip.ecc.size = 256;
fsmc_set_ecc(data->fsmc_device, data->fsmc_slot, ECC_PAGE_256);
break;
case 512:
data->chip.ecc.steps = 1;
data->chip.ecc.size = 512;
fsmc_set_ecc(data->fsmc_device, data->fsmc_slot, ECC_PAGE_512);
break;
case 2048:
data->chip.ecc.steps = 4;
data->chip.ecc.size = 512;
fsmc_set_ecc(data->fsmc_device, data->fsmc_slot, ECC_PAGE_512);
break;
case 4096:
data->chip.ecc.steps =8;
data->chip.ecc.size = 512;
fsmc_set_ecc(data->fsmc_device, data->fsmc_slot, ECC_PAGE_512);
break;
default:
/* page size not handled by HW ECC */
data->chip.ecc.mode = NAND_ECC_NONE;
break;
}
/* second phase of the device scan */
if (nand_scan_tail(&data->mtd)) {
ret = -ENXIO;
goto out;
}
#ifdef CONFIG_MTD_PARTITIONS
if (pdata->chip.part_probe_types) {
ret = parse_mtd_partitions(&data->mtd,
pdata->chip.part_probe_types,
&data->parts, 0);
if (ret > 0) {
add_mtd_partitions(&data->mtd, data->parts, ret);
return 0;
}
}
if (pdata->chip.set_parts)
pdata->chip.set_parts(data->mtd.size, &pdata->chip);
if (pdata->chip.partitions) {
data->parts = pdata->chip.partitions;
ret = add_mtd_partitions(&data->mtd, data->parts,
pdata->chip.nr_partitions);
} else
#endif
ret = add_mtd_device(&data->mtd);
#if defined(CONFIG_MACH_CARTESIO_CHAPLIN) || defined (CONFIG_MACH_CARTESIO_TIGER4) || defined(CONFIG_CARTESIO_NAND_DMA)
mtd = &data->mtd;
chip = &data->chip;
#endif
#if defined(CONFIG_MACH_CARTESIO_CHAPLIN) || defined (CONFIG_MACH_CARTESIO_TIGER4)
/* command to enable and disable Inetrnal ECC*/
//Set features 0xEF command
chip->cmd_ctrl(mtd, 0xEF,NAND_CLE);
//Address command
chip->cmd_ctrl(mtd, 0x90,NAND_ALE);
//Data Command
udelay(chip->chip_delay);
buf[0] = 0;
buf[1] = 0;
buf[2] = 0;
buf[3] = 0;
chip->write_buf(mtd, buf, 4);
status = chip->waitfunc(mtd, chip);
if(status & NAND_STATUS_FAIL)
printk("Error in setting features\n");
#endif
#ifdef CONFIG_CARTESIO_NAND_DMA
init_completion(&data->dma_read_complete);
init_completion(&data->dma_write_complete);
client = (struct pl080_dma_slave*)pdata->ctrl.priv;
data->transfer_addr = res->start + FSMC_NAND_DIO;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
/* Only one channel is allocated as read and write commands
* comes mutually exclusive. Lock is taken by nand_read and
* nand_write command.
*/
data->chan = dma_request_channel(mask, pl080_dma_filter, client);
if (!data->chan) {
printk("unable to get a DMA channel\n");
ret = -EINVAL;
goto out;
}
#endif
if (!ret)
return ret;
nand_release(&data->mtd);
out:
if (pdata->ctrl.remove)
pdata->ctrl.remove(pdev);
platform_set_drvdata(pdev, NULL);
iounmap(data->io_base);
kfree(data);
return ret;
}
3. static int __devexit cartesio_nand_remove(struct platform_device *pdev)/*
* Remove a NAND device
*/
static int __devexit cartesio_nand_remove(struct platform_device *pdev)
{
struct cartesio_nand_data *data = platform_get_drvdata(pdev);
struct platform_nand_data *pdata = pdev->dev.platform_data;
nand_release(&data->mtd);
#ifdef CONFIG_MTD_PARTITIONS
if (data->parts && data->parts != pdata->chip.partitions)
kfree(data->parts);
#endif
#ifdef CONFIG_CARTESIO_NAND_DMA
dma_release_channel(data->chan);
#endif
if (pdata->ctrl.remove)
pdata->ctrl.remove(pdev);
iounmap(data->io_base);
kfree(data);
return 0;
}
4. 一些数据结构:
struct cartesio_nand_data:
struct cartesio_nand_data {
struct nand_chip chip;
struct mtd_info mtd;
void __iomem *io_base;
struct device *fsmc_device;
int fsmc_slot;
#ifdef CONFIG_MTD_PARTITIONS
int nr_parts;
struct mtd_partition *parts;
#endif
#ifdef CONFIG_CARTESIO_NAND_DMA
struct dma_chan *chan;
struct dma_async_tx_descriptor *desc;
dma_addr_t transfer_addr;
struct completion dma_read_complete;
struct completion dma_write_complete;
#endif
};
5. nand_chip的操作函数:
static void cartesio_nand_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
/*
* Control ALE/CLE/nCE and write command and address
*/
static void cartesio_nand_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct cartesio_nand_data *data = chip->priv;
if (cmd == NAND_CMD_NONE)
return;
if (ctrl & NAND_CLE)
writeb(cmd, data->io_base + FSMC_NAND_CLE);
if (ctrl & NAND_ALE) {
writeb(cmd, data->io_base + FSMC_NAND_ALE);
}
}
static int cartesio_nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,uint8_t *buf, int page)
/**
* cartesio_nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
* @page: page number to read
*
* Not for syndrome calculating ecc controllers which need a special oob layout
*/
static int cartesio_nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page)
{
int i, eccsize = chip->ecc.size;
u32 eccbytes = chip->ecc.bytes;
uint32_t *eccpos = chip->ecc.layout->eccpos;
uint8_t *ecc_calc = chip->buffers->ecccalc;
u32 eccsteps = 0;
uint8_t *p = buf;
u32 position1;
u8 error_count;
char temp_buf[eccbytes];
#ifdef CONFIG_CARTESIO_NAND_DMA
int dma_flag = 1;
struct cartesio_nand_data *data = chip->priv;
dma_addr_t dest = 0;
dma_addr_t src = 0;
if(!virt_addr_valid(buf))
dma_flag = 0;
if(dma_flag)
{
dest = dma_map_single(NULL, buf, chip->ecc.steps*eccsize, DMA_FROM_DEVICE);
src = data->transfer_addr;
}
#endif
#ifdef CONFIG_CARTESIO_NAND_DMA
for (i = 0; eccsteps < chip->ecc.steps; i += eccbytes, p += eccsize, dest += eccsize) {
#else
for (i = 0; eccsteps < chip->ecc.steps; i += eccbytes, p += eccsize) {
#endif
chip->ecc.hwctl(mtd, NAND_ECC_READ);
#ifdef CONFIG_CARTESIO_NAND_DMA
if(dma_flag)
{
nand_rx_dma_transfer(mtd, p, eccsize, src, dest);
wait_for_completion_timeout(&data->dma_read_complete,40);
}
else
#endif
chip->read_buf(mtd, p, eccsize);
position1 = mtd->writesize + eccpos[eccbytes*eccsteps];
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, position1, -1);
chip->read_buf(mtd, temp_buf, 16);
eccsteps++;
position1 = eccsteps*eccsize;
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, position1, -1);
chip->ecc.calculate(mtd, p, &ecc_calc[0]);
{
int stat = 0;
error_count = (ecc_calc[12] & ERROR_COUNT_MASK) >> ERROR_COUNT_POS;
if(error_count > 0 && error_count <= ERROR_COUNT_MAX)
stat = nand_correctdata(mtd, p, &ecc_calc[0]);
if (stat)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
}
return 0;
}
#endif
static void cartesio_nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf)
/**
* nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
*/
static void cartesio_nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
{
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *p = (uint8_t *)buf;
uint32_t *eccpos = chip->ecc.layout->eccpos;
struct cartesio_nand_data *data = chip->priv;
int dma_flag = 1;
dma_addr_t src = 0;
dma_addr_t dest = 0;
if(!virt_addr_valid(buf))
dma_flag = 0;
if(dma_flag)
{
src = dma_map_single(NULL, (void *)p, eccsteps*eccsize, DMA_TO_DEVICE);
dest = data->transfer_addr;
}
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize, src += eccsize) {
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
if(dma_flag)
{
nand_tx_dma_transfer(mtd, p, eccsize, src, dest);
wait_for_completion_timeout(&data->dma_write_complete,40);
}
else
chip->write_buf(mtd, p, eccsize);
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
}
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
}
static void cartesio_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)/**
* cartesio_nand_read_buf - [DEFAULT] read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
* @len: number of bytes to read
*
* Default read function for 8bit buswith
*/
static void cartesio_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
struct nand_chip *chip = mtd->priv;
u32 *p = (u32 *)buf;
len >>= 2;
for (i = 0; i < len; i++)
p[i] = readl(chip->IO_ADDR_R);
}
static void cartesio_nand_hwctl(struct mtd_info *mtd, int mode)/*
* Control hardware ECC
*/
static void cartesio_nand_hwctl(struct mtd_info *mtd, int mode)
{
struct nand_chip *chip = mtd->priv;
struct cartesio_nand_data *data = chip->priv;
/*
* Hardware ECC needs to be reset before reading or writing a
* page, otherwise bytes previously read or written in OOB area
* will be erroneously taken into account
*/
fsmc_set_ecc(data->fsmc_device, data->fsmc_slot, ECC_RESET);
}
static int cartesio_nand_calculate(struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc_code)
/*
* Get ECC from hardware
*/
static int cartesio_nand_calculate(struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc_code)
{
struct nand_chip *chip = mtd->priv;
struct cartesio_nand_data *data = chip->priv;
return fsmc_get_parity(data->fsmc_device, data->fsmc_slot, ecc_code);
}
static struct nand_bbt_descr cartesio_nand_bbt_descr/*
* Custom bad block table descriptor coming from Cartesio Bootloader legacy
*/
static uint8_t bbt_pattern[] = { 0xFF };
static struct nand_bbt_descr cartesio_nand_bbt_descr = {
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES | NAND_BBT_SCAN2NDPAGE,
.offs = 0,
.len = 1,
.pattern = bbt_pattern,
};