行工作队列里的mmc_rescan去扫描SD卡,为SD卡上电,发送地址,注册驱动等。考虑到插入SD卡需要一个短时间的过程(有个弹簧卡槽固定住SD卡),如果没有延迟,那么插入SD卡的一瞬间,SD卡还没有完全固定到主板上,系统就开始执行mmc_rescan,那么就很有可能在为SD卡上电、发送地址的过程中出现错误(拔出SD卡同理),因此,必须要有detect_delay这个值。
-> saar_mci_init
这个函数为SD主控制器的探测pin脚申请中断,具体内容将在下文中断的一节中讨论。 static int saar_mci_init(struct device *dev, irq_handler_t saar_detect_int, void *data) {
struct platform_device *pdev = to_platform_device(dev); int cd_irq, gpio_cd; // cd - card detect
saar_mmc_slot[0].gpio_cd = mfp_to_gpio(MFP_PIN_GPIO61); // 将GPIO61设为普通GPIO口
cd_irq = gpio_to_irq(saar_mmc_slot[pdev->id].gpio_cd); // 将GPIO61转换为中断号 gpio_request(gpio_cd, \// 申请GPIO61 gpio_direction_input(gpio_cd); // 将GPIO61设为输入类型 request_irq(cd_irq, saar_detect_int, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, \ ... ... }
得到SD主控制器特有数据后,将其挂载到dev.platform_data下,并最终完成对platform_device *dev的注册。
void __init pxa_register_device(struct platform_device *dev, void *data) {
dev->dev.platform_data = data;
platform_device_register(dev); }
2) 使用platform_data数据
下面就看看SD主控制器是如何使用这些在系统初始化的时候就已经得到的platform_device的数据的,
static int pxamci_probe(struct platform_device *pdev) {
struct mmc_host *mmc;
struct pxamci_host *host = NULL; struct resource *r; int ret, irq;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 得到控制器芯片的起始地址
r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); // 为芯片申请4k的内存空间
irq = platform_get_irq(pdev, 0); // 得到芯片的中断号 host->res = r; host->irq = irq;
host->base = ioremap(r->start, SZ_4K); // 将芯片的物理地址映射为虚拟地址 ... ... }
2.2.3 设备驱动的功能函数 一般情况下,设备驱动里都有一个行为函数结构体,比如字符设备驱动里的struct file_operations *fops,该结构描述了设备所具备的工作能力,比如open, read, write等, struct file_operations { struct module *owner;
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); int (*open) (struct inode *, struct file *); ... ... };
同理,SD主控制器驱动程序里也有一个类似的结构struct mmc_host_ops *ops,它描述了该控制器所具备驱动的能力。
static int pxamci_probe(struct platform_device *pdev) {
mmc->ops = &pxamci_ops; ... ... }
static const struct mmc_host_ops pxamci_ops = { .request = pxamci_request, .get_ro = pxamci_get_ro, .set_ios = pxamci_set_ios,
.enable_sdio_irq = pxamci_enable_sdio_irq, };
其中,(*set_ios)为主控制器设置总线和时钟等配置,(*get_ro)得到只读属性,
(*enable_sdio_irq)开启sdio中断,本文重点讨论(*request)这个回调函数,它是整个SD主控制器驱动的核心,实现了SD主控制器能与SD卡进行通信的能力。 static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq) {
struct pxamci_host *host = mmc_priv(mmc); unsigned int cmdat;
set_mmc_cken(host, 1); host->mrq = mrq; cmdat = host->cmdat;
host->cmdat &= ~CMDAT_INIT; if (mrq->data) {
pxamci_setup_data(host, mrq->data); cmdat &= ~CMDAT_BUSY;
cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; if (mrq->data->flags & MMC_DATA_WRITE) cmdat |= CMDAT_WRITE;
if (mrq->data->flags & MMC_DATA_STREAM) cmdat |= CMDAT_STREAM; }
pxamci_start_cmd(host, mrq->cmd, cmdat); }
其中, pxamci_setup_data()实现数据传输,pxamci_start_cmd()实现指令传输。 至此,我们必须去接触SD主控制器的芯片手册了。
首先,SD主控制器由一系列32位寄存器组成。通过软件的方式,即对寄存器赋值,来控制SD主控制器,进而扮演SD主控制器的角色与SD卡取得通信。
1) cmdat 根据主控制器的芯片手册,寄存器MMC_CMDAT控制命令和数据的传输,具体内容如下,
结合对寄存器MMC_CMDAT的描述,分析代码,
host->cmdat &= ~CMDAT_INIT; // 非初始化状态 if (mrq->data) { // 如果存在数据需要传输
pxamci_setup_data(host, mrq->data); // 实现主控制器与SD卡之间数据的传输 cmdat &= ~CMDAT_BUSY; // 没有忙碌busy信号
cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; // 有数据传输,使用DMA if (mrq->data->flags & MMC_DATA_WRITE)
cmdat |= CMDAT_WRITE; // 设置为写数据 if (mrq->data->flags & MMC_DATA_STREAM)
cmdat |= CMDAT_STREAM; // 设置为数据流stream模式 }
2) pxamci_setup_data 通过DMA实现主控制器与SD卡之间数据的传输 static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) {
host->data = data;
writel(data->blocks, host->base + MMC_NOB); // 设置块的数量
writel(data->blksz, host->base + MMC_BLKLEN); // 设置一个块的大小(一般为512byte) if (data->flags & MMC_DATA_READ) { host->dma_dir = DMA_FROM_DEVICE;
dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG; DRCMR(host->dma_drcmrtx) = 0;
DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD; } else {
host->dma_dir = DMA_TO_DEVICE;
dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC; DRCMR(host->dma_drcmrrx) = 0;
DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD; }
dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma_dir);
for (i = 0; i < host->dma_len; i++) {
unsigned int length = sg_dma_len(&data->sg[i]); host->sg_cpu[i].dcmd = dcmd | length; if (length & 31)