MTD原始设备与NANDFLASH硬件驱动交互

好久没写博客了,今天再次分析NANDFLASH驱动程序,每一次读源码总有一点的收获

1、首先从入口函数开始

probe函数将是我们遇到的第一个与具体硬件打交道,同时也相对复杂的函数对于很多外设的driver来说,只要能成功实现probe函数,那基本上完成这个外设的driver也就成功了一多半,基于MTD的NAND driver就是一个典型的例子。稍后就可以看到,在NAND driver的probe函数中,就已经涉及到了对NAND芯片的读写。
在基于MTD的NAND driver的probe函数中,主要可以分为两部分内容,其一是与很多外设driver类似的一些工作,如申请地址,中断,DMA等资源,kzalloc及初始化一些结构体,分配DMA用的内存等等;其二就是与MTD相关的一

static int s3c24xx_nand_probe(struct platform_device *pdev)
{
/***获取平台数据信息return dev->dev.platform_data;**********/
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
enum s3c_cpu_type cpu_type;
struct s3c2410_nand_info *info;//NANDFLASH控制器信息,在分析驱动源码的时候我发现了一个问题,好像每一个驱动程序都有一个这样类似的结构体,里面封装着各种与该驱动相关的信息**/
struct s3c2410_nand_mtd *nmtd;
struct s3c2410_nand_set *sets;
struct resource *res;
int err = 0;
int size;
int nr_sets;
int setno;
/*****获取芯片CPU类型唯一标识符**/
cpu_type = platform_get_device_id(pdev)->driver_data;

pr_debug("s3c2410_nand_probe(%p)\n", pdev);

info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
dev_err(&pdev->dev, "no memory for flash info\n");
err = -ENOMEM;
goto exit_error;
}
/******把驱动相关的数据信息设置到内核里面*********/
platform_set_drvdata(pdev, info);
/**初始化硬件控制器控制结构**/
spin_lock_init(&info->controller.lock);/**初始化自旋锁**/
init_waitqueue_head(&info->controller.wq);//初始化等待队列

/* get the clock source and enable it */

info->clk = clk_get(&pdev->dev, "nand");/***获取时钟**/
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
err = -ENOENT;
goto exit_error;
}

clk_enable(info->clk);//使能时钟

/* allocate and map the resource */
/****获取平台设备所需要的资源***在arch/arm/plat-samsung/dev-nand.c***********/
/* currently we assume we have the one resource */
res = pdev->resource;
size = resource_size(res);//计算资源的大小
/**为平台设备资源分配空间**/
info->area = request_mem_region(res->start, size, pdev->name);

if (info->area == NULL) {
dev_err(&pdev->dev, "cannot reserve register region\n");
err = -ENOENT;
goto exit_error;
}
/**填充s3c2410_nand_info结构***/
info->device = &pdev->dev;
info->platform = plat; /*保存 s3c2410_platform_nand结构信息*/
info->regs = ioremap(res->start, size);/**映射IO**/
info->cpu_type = cpu_type;

if (info->regs == NULL) {
dev_err(&pdev->dev, "cannot reserve register region\n");
err = -EIO;
goto exit_error;
}

dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);

/* initialise the hardware*/

err = s3c2410_nand_inithw(info);//硬件初始化 主要是配置NFCONF寄存器
if (err != 0)
goto exit_error;

sets = (plat != NULL) ? plat->sets : NULL;
nr_sets = (plat != NULL) ? plat->nr_sets : 1;

info->mtd_count = nr_sets;

/* allocate our information */
/****分配MTD设备结构*****/
size = nr_sets * sizeof(*info->mtds);
info->mtds = kzalloc(size, GFP_KERNEL);
if (info->mtds == NULL) {
dev_err(&pdev->dev, "failed to allocate mtd storage\n");
err = -ENOMEM;
goto exit_error;
}

/* initialise all possible chips */

nmtd = info->mtds;

for (setno = 0; setno < nr_sets; setno++, nmtd++) {
pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
/***填充nand_chip结构**/
s3c2410_nand_init_chip(info, nmtd, sets);
/**扫描nand芯片读取芯片ID等**/
nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
(sets) ? sets->nr_chips : 1,
NULL);

if (nmtd->scan_res == 0) {
s3c2410_nand_update_chip(info, nmtd);
nand_scan_tail(&nmtd->mtd);
s3c2410_nand_add_partition(info, nmtd, sets);
}

if (sets != NULL)
sets++;
}

err = s3c2410_nand_cpufreq_register(info);
if (err < 0) {
dev_err(&pdev->dev, "failed to init cpufreq support\n");
goto exit_error;
}

if (allow_clk_stop(info)) {
dev_info(&pdev->dev, "clock idle support enabled\n");
clk_disable(info->clk);
}

pr_debug("initialised ok\n");
return 0;

exit_error:
s3c24xx_nand_remove(pdev);

if (err == 0)
err = -EINVAL;
return err;
}

static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_mtd *nmtd,
struct s3c2410_nand_set *set)
{
struct nand_chip *chip = &nmtd->chip;
void __iomem *regs = info->regs;

chip->write_buf = s3c2410_nand_write_buf;
chip->read_buf = s3c2410_nand_read_buf;
chip->select_chip = s3c2410_nand_select_chip;
chip->chip_delay = 50;
chip->priv = nmtd;
chip->options = set->options;
chip->controller = &info->controller;

switch (info->cpu_type) {
case TYPE_S3C2410:
chip->IO_ADDR_W = regs + S3C2410_NFDATA;
info->sel_reg = regs + S3C2410_NFCONF;
info->sel_bit = S3C2410_NFCONF_nFCE;
chip->cmd_ctrl = s3c2410_nand_hwcontrol;
chip->dev_ready = s3c2410_nand_devready;
break;

case TYPE_S3C2440:
chip->IO_ADDR_W = regs + S3C2440_NFDATA;
info->sel_reg = regs + S3C2440_NFCONT;
info->sel_bit = S3C2440_NFCONT_nFCE;
chip->cmd_ctrl = s3c2440_nand_hwcontrol;
chip->dev_ready = s3c2440_nand_devready;
chip->read_buf = s3c2440_nand_read_buf;
chip->write_buf = s3c2440_nand_write_buf;
break;

case TYPE_S3C2412:
chip->IO_ADDR_W = regs + S3C2440_NFDATA;
info->sel_reg = regs + S3C2440_NFCONT;
info->sel_bit = S3C2412_NFCONT_nFCE0;
chip->cmd_ctrl = s3c2440_nand_hwcontrol;
chip->dev_ready = s3c2412_nand_devready;

if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT)
dev_info(info->device, "System booted from NAND\n");

break;
}

chip->IO_ADDR_R = chip->IO_ADDR_W;

nmtd->info = info;
nmtd->mtd.priv = chip;
nmtd->mtd.owner = THIS_MODULE;
nmtd->set = set;

if (hardware_ecc) {
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
chip->ecc.correct = s3c2410_nand_correct_data;
chip->ecc.mode = NAND_ECC_HW;

switch (info->cpu_type) {
case TYPE_S3C2410:
chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
break;

case TYPE_S3C2412:
chip->ecc.hwctl = s3c2412_nand_enable_hwecc;
chip->ecc.calculate = s3c2412_nand_calculate_ecc;
break;

case TYPE_S3C2440:
chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
chip->ecc.calculate = s3c2440_nand_calculate_ecc;
break;

}
} else {
chip->ecc.mode = NAND_ECC_SOFT;
}

if (set->ecc_layout != NULL)
chip->ecc.layout = set->ecc_layout;

if (set->disable_ecc)
chip->ecc.mode = NAND_ECC_NONE;

switch (chip->ecc.mode) {
case NAND_ECC_NONE:
dev_info(info->device, "NAND ECC disabled\n");
break;
case NAND_ECC_SOFT:
dev_info(info->device, "NAND soft ECC\n");
break;
case NAND_ECC_HW:
dev_info(info->device, "NAND hardware ECC\n");
break;
default:
dev_info(info->device, "NAND ECC UNKNOWN\n");
break;
}

/* If you use u-boot BBT creation code, specifying this flag will
* let the kernel fish out the BBT from the NAND, and also skip the
* full NAND scan that can take 1/2s or so. Little things... */
if (set->flash_bbt)
chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值