mmc驱动
来源:互联网 发布:centos退出root 编辑:程序博客网 时间:2024/05/29 10:01
MMC子系统的代码在kernel/driver/MMC下.MMC子系统范围三个部分:
HOST部分是针对不同主机的驱动程序,这一部是驱动程序工程师需要根据自己的特点平台来完成的。
CORE部分:这是整个MMC的核心存,这部分完成了不同协议和规范的实现,并为HOST层的驱动提供了接口函数。
CARD部分:因为这些记忆卡都是块设备,当然需要提供块设备的驱动程序,这部分就是实现了将你的SD卡如何实现为块设备的。
1.HOST层分析:
HOST层实现的就是我们针对特定主机的驱动程序,我们先采用platform_driver_register(&&atxx_sdhc_drv)注册了一个平台设备,接下来重点关注probe函数。在这个函数中,我们与CORE的联系是通过下面三句实现的。
首先分配一个mmc_host结构体,
mmc = mmc_alloc_host(sizeof(atxx_host_t), &dev->dev);注意sizeof(atxx_host_t),这样就能在mmc_host中找到了atxx_host。
接下来为mmc->ops赋值,mmc->ops = &atxx_ops;atxx_ops结构实现了几个很重要的函数。中间还对mmc结构的很多成员进行了赋值,最后将mmc结构加入到MMC子系统,mmc_alloc_host,以及mmc_add_host的具体做了什么事情,我们在下节再分析,这三句是些MMC层驱动必须包含的。
static struct mmc_host_ops atxx_ops =
{
.enable = atxx_enable,
.disable = atxx_disable,
.request = atxx_request,
.set_ios = atxx_set_ios,
.get_ro = atxx_get_ro,
.get_cd = atxx_get_cd,
.enable_sdio_irq = atxx_enable_sdio_irq,
};
atxx_get_ro:判断我们的卡是否是写保护的
atxx_get_cd:判断卡是否存在
atxx_set_ios:atxx_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
依据核心层传递过来的ios,来设置硬件IO,包括引脚配置,使能时钟,和配置总线带宽。
atxx_request:这个函数是最主要,也最复杂的函数,实现了命令和数据的发送和接收,
当CORE部分需要发送命令或者传输数据时,都会调用这个函数,并传递mrq请求。
static void atxx_request(struct mmc_host *mmc, struct mmc_request *req)
{
atxx_host_t *host;
host = mmc_priv(mmc);
host->mrq = req;
PDEBUG("atxx_request:cmd %d,arg 0x%x,resp 0x%x.\n",req->cmd->opcode,
req->cmd->arg,req->cmd->resp[0]);
if (card_present(host) == 0) {
PDEBUG("card is out\n");
req->cmd->error = -ENOMEDIUM;
host->mrq = NULL;
mmc_request_done(host->mmc,req); //如果卡不存在就终止请求
return;
}
if (atxx_setup_cmd(host,req->cmd) != 0) {
PERROR("atxx_setup_cmd failed\n");
req->cmd->error = -EIO;
host->mrq = NULL;
mmc_request_done(host->mmc,req);
return;
}
return;
}
Atxx_host.c分析:
static int __init atxx_sdhc_init(void)
{
return platform_driver_register(&atxx_sdhc_drv);
}
注册平台驱动
static struct platform_driver atxx_sdhc_drv =
{
.driver = {
.name = "sdhc",
.owner = THIS_MODULE,
.pm = &atxx_sdhc_pm_ops,
},
.probe = atxx_sdhc_probe,
.remove = atxx_sdhc_remove,
.shutdown = atxx_sdhc_shutdown,
};
.probe = atxx_sdhc_probe,是核心,由于host与硬件是直接相关的,probe接下来将做部分关于硬件初始化的工作
进入函数atxx_sdhc_probe
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
irq = platform_get_resource(dev, IORESOURCE_IRQ, 0)->start;
获取平台资源
mmc = mmc_alloc_host(sizeof(atxx_host_t), &dev->dev);分配一个mmc的控制器
host = mmc_priv(mmc);
host->sdhc.controller_id = dev->id;
host->pdev = dev;
ret=request_threaded_irq(irq,atxx_sdhc_irq_handle,atxx_sdhc_irq_thread, IRQF_DISABLED, host_names[dev->id], host);//中断申请
host->mmc = mmc;
host->irq = irq;
host->sdhc.ctx = host
对atxx_host_t这个私有结构的配置
if (host->sdhc.controller_id == SYNOP_MEM_CARD) {
host->clk_gate = clk_get(&dev->dev,"a_sd");
} else {
host->clk_gate = clk_get(&dev->dev,"a_sdio");
}
clk_enable(host->clk_gate);获取时钟并使能 ????时钟从哪里获取,怎么获取的
mmc->ops = &atxx_ops;向core层提供的接口函数集
mmc->ocr_avail = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_30_31
| MMC_VDD_32_33 | MMC_VDD_33_34;
#ifdef CONFIG_ATXX_SDHC_4BITS
PINFO("SD bus width is set to 4bits\n");
mmc->caps = MMC_CAP_SDIO_IRQ | MMC_CAP_4_BIT_DATA;
#else
mmc->caps = MMC_CAP_SDIO_IRQ;
#endif
mmc->f_min = 125000;
mmc->f_max = 26000000;
#if defined(CONFIG_MACH_T3C)
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
mmc->f_max = 52000000;
#endif
mmc->max_hw_segs = MAX_DESCRIPTOR_NUM;
mmc->max_phys_segs = MAX_DESCRIPTOR_NUM;
mmc->max_req_size = MAX_DESCRIPTOR_NUM * PAGE_SIZE;
mmc->max_seg_size = PAGE_SIZE;
mmc->max_blk_size = 65535;
mmc->max_blk_count = 65535;
PDEBUG("max_segs %d, max_size %d,max_blk_size %d\n",mmc->max_hw_segs,mmc->max_seg_size,mmc->max_blk_size);
向mmc_host结构体赋值
host->dma_desc = dma_alloc_coherent(NULL,
sizeof(struct DES) * MAX_DESCRIPTOR_NUM,
&host->dma_desc_phy, GFP_KERNEL);
Dma映射
platform_set_drvdata(dev, mmc);
mmc_add_host(mmc);增加设备类并启动host
mmc_add_host分析:
int mmc_add_host(struct mmc_host *host)
{
int err;
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
!host->ops->enable_sdio_irq);
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
err = device_add(&host->class_dev);
if (err)
return err;
#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
#endif
mmc_start_host(host); //开启主机控制器
return 0;
}
void mmc_start_host(struct mmc_host *host)
{
mmc_power_off(host);
mmc_detect_change(host, 0);
}
Mmc_delect_change函数中调用mmc_schedule_delayed_work(&host->detect, delay);作用是延时delay后就会调用mmc_rescan函数,由于delay=0,即相当于直接调用mmc_rescan函数
进入mmc_rescan函数
if (host->ops->get_cd && host->ops->get_cd(host) == 0)
goto out;
检测是否有sd卡存在
host->ops->get_cd(host)
Get_cd对应atxx_ops结构中的.get_cd = atxx_get_cd,
mmc_claim_host(host);获取主机控制权
mmc_power_up(host);设置并开启主机控制器
mmc_go_idle(host);是sd卡进入idle状态
err = mmc_send_app_op_cond(host, 0, &ocr);对sd卡的设置
if (mmc_attach_sd(host, ocr))
函数mmc_attach_sd开始对sd的初始化
其中mmc_sd_attach_bus_ops(host);完成bus_ops与sd的绑定
host->ocr = mmc_select_voltage(host, ocr);这是根据host的电压范围和读取到的SD卡工作电压,来判断工作电压是否匹配。
err = mmc_sd_init_card(host, host->ocr, NULL);这个函数完成了整个SD卡初始化的全动作
函数mmc_sd_init_card中在调用mmc_all_send_cid函数
memcpy(cid, cmd.resp, sizeof(u32) * 4);拷贝身份信息
函数mmc_sd_init_card继续调用card = mmc_alloc_card(host, &sd_type);
card->dev.parent = mmc_classdev(host);
card->dev.bus = &mmc_bus_type;
card->dev.release = mmc_release_card;
card->dev.type = type;
sd卡devices信息与core层的driver相匹配
- mmc驱动
- MMC驱动之mmc host
- MMC卡驱动分析
- MMC/SD驱动(1)
- MMC 卡驱动分析
- MMC 卡驱动分析
- mmc驱动工作流程
- MMC 卡驱动分析
- MMC 卡驱动分析
- MMC 卡驱动分析
- MMC卡驱动分析
- MMC 卡驱动分析
- MMC卡驱动分析
- MMC 卡驱动分析
- MMC 驱动分析
- MMC 卡驱动分析
- MMC 卡驱动分析
- linux mmc 驱动结构
- 套接字连接中断:The socket connection was aborted
- 个人心得:wince学习路线
- u-boot中的常用指令汇总
- SQL Server绑定连接的类型
- JAVA字符串的一些特殊应用——数字变成字符串并位数补足0等
- mmc驱动
- android常用 shell指令
- UISlider
- google开源的c++项目
- SQL Server数据库查询优化的常用方法总结
- Ubuntu11.10编译内核:make xconfig错误
- 解决ActivityGroup的sub Activity中使用spinner出现的WindowManager$BadTokenException问题
- Java 子父类怪异关系(一)
- windows系统上安装与使用Android NDK r5