博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
sdio 驱动
阅读量:4225 次
发布时间:2019-05-26

本文共 5048 字,大约阅读时间需要 16 分钟。

1 确定关注范围

根据公司代码编译出的原始.o 文件,可以间接确定真正起作用的源文件包括(也可以通过 Makefile 来直接确定)。

1) <kernel/driver/mmc/card> 

card目录下的驱动文件是卡的设备驱动,也就是针对mmc或者sd卡的块设备驱动

block.c queue.c mmc_test.c sdio_card.c

<block.c>

static int __init mmc_blk_init(void){...        res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");...        res = mmc_register_driver(&mmc_driver);...}

register_blkdev为注册一个块设备,mmc_register_driver为注册一个mmc驱动。mmc_driver中最重要的函数:mmc_blk_probe,用于找到匹配的mmc_card。

<card.h>

struct mmc_card {        struct mmc_host         *host;...}

host 指针指向一个 mmc 主机实例,块设备中的读写操作就是调用这个mmc主机的操作函数host->ops->request来实现对实际硬件的操作。要找到这mmc_card,就得先把mmc_card这个设备挂载到mmc_bus去。mmc_bus 的注册在第二部分实现,mmc_card的挂载在第三部分实现。

2) <kernel/driver/mmc/core>

core 目录下的驱动文件是 mmc 总线驱动程序

core.c host.c mmc.c quirks.c sdio_cis.c sdio_irq.c sdio_ops.c sd_ops.c bus.c debugfs.c mmc_ops.c sdio_bus.c sdio_io.c sdio.c sd.c

<core.c>

static int __init mmc_init(void){...        workqueue = alloc_ordered_workqueue("kmmcd", 0);...        ret = mmc_register_bus();...        ret = mmc_register_host_class();...        ret = sdio_register_bus();...}

这个函数一开始建立了一个工作队列workqueue,这个工作队列的作用主要是用来支持热插拔;然后分别注册一个mmc总线mmc_bus_type,一个mmc_host类,一个 sdio总线 sdio_bus_type

3) < kernel/driver/mmc/host>

host目录下的驱动文件是mmc或者sd卡的通讯接口驱动

scxxxx.c sdhci.c

<scxxxx.c>

static int __init sdhci_sprd_init(void){        return platform_driver_register(&sdhci_sprd_driver);}

该函数只是注册了一个 platform 总线上的驱动,由于我们的 mmc 是通过 platform 总线上的设备 mmc_host 接口来通讯的,所以这个驱动实则上就是最底层的硬件接口驱动。当然,在这之前,我们得注册一个 platform 总线,并在该 platform 总线上挂载了一个 mmc_host 接口设备。这样,在注册 platform 驱动的时候,就会调用该驱动中的 probe 函数。Probe函数中调用了函数:

host = sdhci_alloc_host(dev, sizeof(struct sprd_host_data));//申请 private 结构体 sprd_host_data host_data = sdhci_priv(host);//赋值为 host->private
sdhci_host结构体的最后一个成员为:unsigned long private[0] , private 指向sdhci_host结构体之后的地址,private本身并不分配空间。所以,通过host->private即可访问host_data.此为内核中的常用机制。
进一步调用:
mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
创建一个mmc_host结构,这个结构就是在mmc_card所需要的mmc主机实例。
host = mmc_priv(mmc);host->mmc = mmc;
在mmc_alloc_host中,创建一个mmc_host和 sdhci_host 和 sprd_host_data,且mmc_host的最后一个成员指针private指向 sdhci_host; sdhci_host 的 mmc指针指向mmc_host。
除此之外,mmc_alloc_host()最重要的作用是建立了一个工作队列任务
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
工作队列任务执行的函数为mmc_rescan,在这个函数中以不同的频率扫描:
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq){...        mmc_power_up(host);...        mmc_hw_reset_for_init(host);...        sdio_reset(host);        mmc_go_idle(host);        mmc_send_if_cond(host, host->ocr_avail);...        if (!mmc_attach_sdio(host))                return 0;        if (!host->ios.vdd)                mmc_power_up(host);        if (!mmc_attach_sd(host))                return 0;        if (!host->ios.vdd)                mmc_power_up(host);        if (!mmc_attach_mmc(host))                return 0;        mmc_power_off(host);...}
从这个函数看到,一开始就是设置某一个时钟频率,然后对mmc或者sd发送一些命令进行探测。
接着看mmc_attach_mmc()
int mmc_attach_mmc(struct mmc_host *host){...err = mmc_init_card(host, host->ocr, NULL);...err = mmc_add_card(host->card);...}
一开始设置mmc的电压,然后就对mmc卡进行初始化,主要是读取mmc卡里的一些寄存器信息,且对这些寄存器的值进行设置。然后,调用mmc_add_card来把mmc_card挂载到mmc_bus_type总线上去:
int mmc_add_card(struct mmc_card *card){...ret = device_add(&card->dev);...}
接着调用 device_add()
int device_add(struct device *dev){...bus_probe_device(dev);...}
然后调用bus_probe_device()
void bus_probe_device(struct device *dev){...ret = device_attach(dev);...}
这样,在总线mmc_bus_type中就有了mmc设备mmc_card了。
probe 函数的一个重要初始化操作:
host->ops = &sdhci_sprd_ops;
这个 sdhci_sprd_ops 操作函数就是 sdhci 接口的操作函数。
不过这里有个问题,就是这个工作队列任务什么时候开始执行呢,又与 (1) 部分创建的工作队列workqueue有什么关系,跟热插拔有什么关系。

继续把焦点放到probe函数:

{...host_data->platdata = dev_get_platdata(dev);...sd_detect_gpio = host_data->platdata->detect_gpio;...detect_irq = gpio_to_irq(sd_detect_gpio);...host_data->detect_irq = detect_irq;...ret = sdhci_add_host(host);...}

这里有个 detect_irq 的变量,保存了设备中断引脚号。当sd卡插入时检测到一个外部中断。

int sdhci_add_host(struct sdhci_host *host){...host_data = sdhci_priv(host);detect_irq = host_data->detect_irq;if (sdcard_present(host))	{		ret = request_threaded_irq(detect_irq, NULL, sd_detect_irq,			IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "sd card detect", host);	} else 	{		ret = request_threaded_irq(detect_irq, NULL, sd_detect_irq,			IRQF_TRIGGER_LOW | IRQF_ONESHOT, "sd card detect", host);	}...sdhci_init(host, 0);...mmc_add_host(mmc);...sdhci_enable_card_detection(host);...}

打开SD卡检测

static void sdhci_enable_card_detection(struct sdhci_host *host){	sdhci_set_card_detection(host, true);}
进入sdhci_set_card_detection()
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable){	int irq;	struct sprd_host_data *host_data;	host_data = sdhci_priv(host);	irq = host_data->detect_irq;	if(irq > 0){		if(!enable) {			irq_set_irq_type(irq,IRQF_TRIGGER_NONE);			return;		}		if(sdcard_present(host)){			irq_set_irq_type(irq,IRQF_TRIGGER_HIGH);		}else{			irq_set_irq_type(irq,IRQF_TRIGGER_LOW);		}	}}

<block.c> module_init(mmc_blk_init);
<mmc_test.c> module_init(mmc_test_init);
<scxxxx.c> module_init(sdhci_sprd_init);
<sdhci.c> module_init(sdhci_drv_init);

转载地址:http://fhbqi.baihongyu.com/

你可能感兴趣的文章
Linux内核同步机制API函数:宏:spin_lock_init ( )
查看>>
driver_register 理解
查看>>
copy_from_user && copy_to_user
查看>>
device_register
查看>>
Android上C++对象的自动回收机制分析
查看>>
从spin_lock到spin_lock_irqsave
查看>>
sdio 驱动
查看>>
vim 常用用法
查看>>
更好就足够了吗?| 驱动变革
查看>>
技术选型指南
查看>>
在一家技术公司做媒体
查看>>
项目管理的三个关键
查看>>
从技术雷达看DevOps十年-DevOps和持续交付
查看>>
从架构可视化入门到抽象坏味道
查看>>
重读领域驱动设计——如何说好一门通用语言
查看>>
不就是个短信登录API嘛,有这么复杂吗?
查看>>
第二十期技术雷达正式发布——给你有态度的技术解析
查看>>
从技术雷达看DevOps的十年 – 基础设施即代码和云计算
查看>>
Scala 3 不再支持 XML 了吗?
查看>>
微前端如何落地?
查看>>