cartesio_nand.c

本文深入分析了CARTESIO-NAND驱动的实现细节,包括初始化、退出、探针函数以及NAND设备操作函数。重点介绍了如何在驱动中配置设备属性、初始化NAND芯片、设置ECC页面大小、读写保护等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这是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,
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值