sdio 驱动

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);









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值