linux设备驱动-SD卡驱动详解3host层
HOST 部分是针对不同主机的驱动程序,提供与各芯片构架相关的文件,这一部是驱动程序工程师需要根据自己的特点平台来完成的。card(区块层) 与core(核心层)是linux系统封装好了部分,不需要修改。
核心层根据需要构造各种MMC/SD命令,这些命令怎么发送给MMC/SD卡呢?这通过主机控制器层来实现。这层是架构相关的,里面针对各款CPU提供一个文件,目前支持的CPU还很少。
以:drivers\mmc\host\s3cmci.c为例
1 driver init
从driver init开始看起
1 static struct platform_driver s3cmci_driver = { 2 .driver = { 3 .name = "s3c-sdi", 4 .owner = THIS_MODULE, 5 .pm = s3cmci_pm_ops, 6 }, 7 .id_table = s3cmci_driver_ids, 8 .probe = s3cmci_probe, 9 .remove = s3cmci_remove, 10 .shutdown = s3cmci_shutdown, 11 }; 12 13 module_platform_driver(s3cmci_driver);
2 probe函数
它首先进行一些低层设置,比如设置MMC/SD/SDIO控制器使用到的CPIO引脚、使能控制器、注册中断处理函数等,然后向上面的核心层增加一个主机(Host)。
概括地讲,主要作用:
(1)初始化设备的数据结构,并将数据挂载到pdev->dev.driver_data下;
(2)实现设备驱动的功能函数,如mmc->ops = &s3cmci_ops;
(3)申请中断函数request_irq();
(4)注册设备,即注册kobject,建立sys文件,发送uevent;
(5)其他需求,如在/proc/driver下建立用户交互文件等
1 static int s3cmci_probe(struct platform_device *pdev) 2 { 3 struct s3cmci_host *host; 4 struct mmc_host *mmc; 5 int ret; 6 int is2440; 7 int i; 8 9 is2440 = platform_get_device_id(pdev)->driver_data; 10 11 mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);// 12 if (!mmc) { 13 ret = -ENOMEM; 14 goto probe_out; 15 } 16 17 for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) { 18 ret = gpio_request(i, dev_name(&pdev->dev));// 19 if (ret) { 20 dev_err(&pdev->dev, "failed to get gpio %d\n", i); 21 22 for (i--; i >= S3C2410_GPE(5); i--) 23 gpio_free(i); 24 25 goto probe_free_host; 26 } 27 } 28 29 host = mmc_priv(mmc); 30 host->mmc = mmc; 31 host->pdev = pdev; 32 host->is2440 = is2440; 33 34 host->pdata = pdev->dev.platform_data; 35 if (!host->pdata) { 36 pdev->dev.platform_data = &s3cmci_def_pdata; 37 host->pdata = &s3cmci_def_pdata; 38 } 39 40 spin_lock_init(&host->complete_lock); 41 tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host); 42 43 if (is2440) { 44 host->sdiimsk = S3C2440_SDIIMSK; 45 host->sdidata = S3C2440_SDIDATA; 46 host->clk_div = 1; 47 } else { 48 host->sdiimsk = S3C2410_SDIIMSK; 49 host->sdidata = S3C2410_SDIDATA; 50 host->clk_div = 2; 51 } 52 53 host->complete_what = COMPLETION_NONE; 54 host->pio_active = XFER_NONE; 55 56 #ifdef CONFIG_MMC_S3C_PIODMA 57 host->dodma = host->pdata->use_dma;//同时使能了PIO和DMA模式的情 58 #endif 59 60 host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取IO 资源 61 if (!host->mem) { 62 dev_err(&pdev->dev, 63 "failed to get io memory region resource.\n"); 64 65 ret = -ENOENT; 66 goto probe_free_gpio; 67 } 68 69 host->mem = request_mem_region(host->mem->start, 70 resource_size(host->mem), pdev->name);// 71 72 if (!host->mem) { 73 dev_err(&pdev->dev, "failed to request io memory region.\n"); 74 ret = -ENOENT; 75 goto probe_free_gpio; 76 } 77 78 host->base = ioremap(host->mem->start, resource_size(host->mem));//IO映射,将实地址映射为内核虚拟地址使用.host->base将保持SDI寄存器基地址所对应的内核虚拟地址。 79 if (!host->base) { 80 dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); 81 ret = -EINVAL; 82 goto probe_free_mem_region; 83 } 84 85 host->irq = platform_get_irq(pdev, 0);//中断资源的申请 86 if (host->irq == 0) { 87 dev_err(&pdev->dev, "failed to get interrupt resource.\n"); 88 ret = -EINVAL; 89 goto probe_iounmap; 90 } 91 92 if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {// 93 dev_err(&pdev->dev, "failed to request mci interrupt.\n");// 94 ret = -ENOENT; 95 goto probe_iounmap; 96 } 97 98 /* We get spurious interrupts even when we have set the IMSK 99 * register to ignore everything, so use disable_irq() to make 100 * ensure we don't lock the system with un-serviceable requests. */ 101 102 disable_irq(host->irq); 103 host->irq_state = false; 104 105 if (!host->pdata->no_detect) { 106 ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect"); 107 if (ret) { 108 dev_err(&pdev->dev, "failed to get detect gpio\n"); 109 goto probe_free_irq; 110 } 111 112 host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);//获取这个GPIO的外部中断向量号,可见后面的SD卡的检测可能会用到这个引脚的外部中断。 113 114 if (host->irq_cd >= 0) { 115 if (request_irq(host->irq_cd, s3cmci_irq_cd,//申请中断,s3cmci_irq_cd是其处理函数 116 IRQF_TRIGGER_RISING | 117 IRQF_TRIGGER_FALLING, 118 DRIVER_NAME, host)) {// 119 dev_err(&pdev->dev, 120 "can't get card detect irq.\n"); 121 ret = -ENOENT; 122 goto probe_free_gpio_cd; 123 } 124 } else { 125 dev_warn(&pdev->dev, 126 "host detect has no irq available\n"); 127 gpio_direction_input(host->pdata->gpio_detect); 128 } 129 } else 130 host->irq_cd = -1; 131 132 if (!host->pdata->no_wprotect) { 133 ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp"); 134 if (ret) { 135 dev_err(&pdev->dev, "failed to get writeprotect\n"); 136 goto probe_free_irq_cd; 137 } 138 139 gpio_direction_input(host->pdata->gpio_wprotect); 140 } 141 142 /* depending on the dma state, get a dma channel to use. */ 143 144 if (s3cmci_host_usedma(host)) { 145 host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,//申请DMA 通道,对s3c2440 平台有4 通道的DMA。而DMACH_SDI 是一个虚拟的通道号 146 host); 147 if (host->dma < 0) { 148 dev_err(&pdev->dev, "cannot get DMA channel.\n"); 149 if (!s3cmci_host_canpio()) { 150 ret = -EBUSY; 151 goto probe_free_gpio_wp; 152 } else { 153 dev_warn(&pdev->dev, "falling back to PIO.\n"); 154 host->dodma = 0; 155 } 156 } 157 } 158 159 host->clk = clk_get(&pdev->dev, "sdi");//sdi时钟和波特率的有关设置 160 if (IS_ERR(host->clk)) { 161 dev_err(&pdev->dev, "failed to find clock source.\n"); 162 ret = PTR_ERR(host->clk); 163 host->clk = NULL; 164 goto probe_free_dma; 165 } 166 167 ret = clk_enable(host->clk); 168 if (ret) { 169 dev_err(&pdev->dev, "failed to enable clock source.\n"); 170 goto clk_free; 171 } 172 173 host->clk_rate = clk_get_rate(host->clk); 174 /*对这个将要出嫁的mmc_host进行最后的设置,mmc->ops = &s3cmci_ops; 就是一直以来向core层提供的接口函数集。后面的分析可能部分是围绕它其中的函数展开的*/ 175 mmc->ops = &s3cmci_ops; 176 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 177 #ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ 178 mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; 179 #else 180 mmc->caps = MMC_CAP_4_BIT_DATA; 181 #endif 182 mmc->f_min = host->clk_rate / (host->clk_div * 256); 183 mmc->f_max = host->clk_rate / host->clk_div; 184 185 if (host->pdata->ocr_avail) 186 mmc->ocr_avail = host->pdata->ocr_avail; 187 188 mmc->max_blk_count = 4095; 189 mmc->max_blk_size = 4095; 190 mmc->max_req_size = 4095 * 512; 191 mmc->max_seg_size = mmc->max_req_size; 192 193 mmc->max_segs = 128; 194 195 dbg(host, dbg_debug, 196 "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n", 197 (host->is2440?"2440":""), 198 host->base, host->irq, host->irq_cd, host->dma); 199 200 ret = s3cmci_cpufreq_register(host);//使用的是Linux的通告机制 201 if (ret) { 202 dev_err(&pdev->dev, "failed to register cpufreq\n"); 203 goto free_dmabuf; 204 } 205 206 ret = mmc_add_host(mmc);//注册host 207 if (ret) { 208 dev_err(&pdev->dev, "failed to add mmc host.\n"); 209 goto free_cpufreq; 210 } 211 212 s3cmci_debugfs_attach(host); 213 214 platform_set_drvdata(pdev, mmc); 215 dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc), 216 s3cmci_host_usedma(host) ? "dma" : "pio", 217 mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw"); 218 219 return 0; 220 221 free_cpufreq: 222 s3cmci_cpufreq_deregister(host); 223 224 free_dmabuf: 225 clk_disable(host->clk); 226 227 clk_free: 228 clk_put(host->clk); 229 230 probe_free_dma: 231 if (s3cmci_host_usedma(host)) 232 s3c2410_dma_free(host->dma, &s3cmci_dma_client); 233 234 probe_free_gpio_wp: 235 if (!host->pdata->no_wprotect) 236 gpio_free(host->pdata->gpio_wprotect); 237 238 probe_free_gpio_cd: 239 if (!host->pdata->no_detect) 240 gpio_free(host->pdata->gpio_detect); 241 242 probe_free_irq_cd: 243 if (host->irq_cd >= 0) 244 free_irq(host->irq_cd, host); 245 246 probe_free_irq: 247 free_irq(host->irq, host); 248 249 probe_iounmap: 250 iounmap(host->base); 251 252 probe_free_mem_region: 253 release_mem_region(host->mem->start, resource_size(host->mem)); 254 255 probe_free_gpio: 256 for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) 257 gpio_free(i); 258 259 probe_free_host: 260 mmc_free_host(mmc); 261 262 probe_out: 263 return ret; 264 }
2.1 分配mmc host
定义位于drivers\mmc\core\host.c
1 struct mmc_host *mmc_alloc_host(int extra, struct device *dev) 2 { 3 int err; 4 struct mmc_host *host; 5 6 host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);//分配mmc_host结构体 7 if (!host) 8 return NULL; 9 10 /* scanning will be enabled when we're ready */ 11 host->rescan_disable = 1; 12 idr_preload(GFP_KERNEL); 13 spin_lock(&mmc_host_lock); 14 err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT); 15 if (err >= 0) 16 host->index = err; 17 spin_unlock(&mmc_host_lock); 18 idr_preload_end(); 19 if (err < 0) 20 goto free; 21 22 dev_set_name(&host->class_dev, "mmc%d", host->index); 23 24 host->parent = dev; 25 host->class_dev.parent = dev; 26 host->class_dev.class = &mmc_host_class; 27 device_initialize(&host->class_dev); 28 29 mmc_host_clk_init(host); 30 31 mutex_init(&host->slot.lock); 32 host->slot.cd_irq = -EINVAL; 33 34 spin_lock_init(&host->lock); 35 init_waitqueue_head(&host->wq); 36 INIT_DELAYED_WORK(&host->detect, mmc_rescan);//将mmc_scan函数加入工作队列 37 #ifdef CONFIG_PM 38 host->pm_notify.notifier_call = mmc_pm_notify; 39 #endif 40 41 /* 42 * By default, hosts do not support SGIO or large requests. 43 * They have to set these according to their abilities. 44 */ 45 host->max_segs = 1; 46 host->max_seg_size = PAGE_CACHE_SIZE; 47 48 host->max_req_size = PAGE_CACHE_SIZE; 49 host->max_blk_size = 512; 50 host->max_blk_count = PAGE_CACHE_SIZE / 512; 51 52 return host; 53 54 free: 55 kfree(host); 56 return NULL; 57 }
mmc_host结构体如下,位于:include\linux\mmc\host.h
用来描述卡控制器位, 结构体mmc_host定义于/include/linux/mmc/host.c,可以认为是linux为SD卡控制器专门准备的一个类,该类里面的成员是所有SD卡控制器都需要的,放之四海而皆准的数据结构,在本例芯片控制器的驱动程序s3cmci.c中,则为该类具体化了一个对象struct mmc_host *mmc,此mmc指针即指代着该ARM芯片SD卡控制器的一个具体化对象(可以看出虽然C是面向过程的语言,但还是用到了一些面向对象思想的)。
1 struct mmc_host { 2 struct device *parent; 3 struct device class_dev; 4 int index; 5 const struct mmc_host_ops *ops;//SD卡主控制器的操作函数,即该控制器所具备的驱动能力 6 unsigned int f_min; 7 unsigned int f_max; 8 unsigned int f_init; 9 u32 ocr_avail; 10 u32 ocr_avail_sdio; /* SDIO-specific OCR */ 11 u32 ocr_avail_sd; /* SD-specific OCR */ 12 u32 ocr_avail_mmc; /* MMC-specific OCR */ 13 struct notifier_block pm_notify; 14 u32 max_current_330; 15 u32 max_current_300; 16 u32 max_current_180; 17 ... 18 const struct mmc_bus_ops *bus_ops; /* current bus driverSD总线驱动的操作函数,即SD总线所具备的驱动能力 */ 19 ... 20 struct mmc_ios ios; // 配置时钟、总线、电源、片选、时序等 21 .. 22 struct mmc_card *card; // 连接到此主控制器的SD卡设备 23 ... 24 }
结构体s3cmci_host ,定义位于:drivers\mmc\host\s3cmci.h
1 struct s3cmci_host { 2 struct platform_device *pdev; 3 struct s3c24xx_mci_pdata *pdata; 4 struct mmc_host *mmc; 5 struct resource *mem; 6 struct clk *clk; 7 void __iomem *base; 8 int irq; 9 int irq_cd; 10 int dma; 11 ... 12 }
本例中struct mmc_host_ops *ops定义
1 static struct mmc_host_ops s3cmci_ops = { 2 .request = s3cmci_request, 3 .set_ios = s3cmci_set_ios, 4 .get_ro = s3cmci_get_ro, 5 .get_cd = s3cmci_card_present, 6 .enable_sdio_irq = s3cmci_enable_sdio_irq, 7 };
2.2 tasklet_init函数:
tasklet就象一个内核定时器, 在一个"软中断"的上下文中执行(以原子模式),常用在硬件中断处理中,可以使得复杂的任务安全地延后到以后的时间处理。task_init 建立一个tasklet,然后调用函数 tasklet_schedule将这个tasklet放在 tasklet_vec链表的头部,并唤醒后台线程 ksoftirqd。当后台线程ksoftirqd 运行调用__do_softirq 时,会执行在中断向量表softirq_vec里中断号TASKLET_SOFTIRQ对应的 tasklet_action函数,然后 tasklet_action遍历 tasklet_vec链表,调用每个 tasklet的函数完成软中断操作,上面例子中即是pio_tasklet函数,另外软中断处理函数只能传递一个long型变量。这里是直接使用host的地址,作为传递参数。关于这个pio_tasklet现在说他还为时过早,等时辰一到自然会对他大书特书。
1 if (is2440) { 2 host->sdiimsk = S3C2440_SDIIMSK; 3 host->sdidata = S3C2440_SDIDATA; 4 host->clk_div = 1; 5 } else { 6 host->sdiimsk = S3C2410_SDIIMSK; 7 host->sdidata = S3C2410_SDIDATA; 8 host->clk_div = 2; 9 } 10 11 host->complete_what = COMPLETION_NONE; 12 host->pio_active = XFER_NONE;
host->sdiimsk 、host->sdidata分别用来存放host控制器SDI中断屏蔽寄存器和SDI数据寄存器相对SDI 寄存器的偏移地址。对于s3c2440 根据芯片手册SDIIntMsk 偏移地址为0x3C,SDIDAT偏移地址为0x40。最后host->clk_div就是指的SDI使用的时钟分频系数了。
host->complete_what 是一个枚举类型变量,实际上用以标示传输完成的状态;host->pio_active标示数据传输的方向,所以在这里一起初始化为空。
2.3 s3cmci_cpufreq_register
1 static inline int s3cmci_cpufreq_register(struct s3cmci_host *host) { 2 3 host->freq_transition.notifier_call = s3cmci_cpufreq_transition; 4 5 return cpufreq_register_notifier(&host->freq_transition, 6 7 CPUFREQ_TRANSITION_NOTIFIER); 8 }
cpufreq_register_notifier cpu是将host->freq_transition注册到CPU频率通告链上,这个是由内核维护的,当cpu频率改变时将会调用上面注册的s3cmci_cpufreq_transition的内容。
2.4 mmc_add_host
定义位于:drivers\mmc\core\host.c
它的功能就是通过device_add函数将设备注册进linux设备模型,最终的结果就是在sys/bus/platform/devices目录下能见到s3c-sdhci.1,s3c-sdhci.2,s3c-sdhci.3设备节点。
1 int mmc_add_host(struct mmc_host *host) 2 { 3 int err; 4 5 WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && 6 !host->ops->enable_sdio_irq); 7 8 err = device_add(&host->class_dev);//将设备注册进linux设备模型,最终的结果就是在sys/bus/platform/devices目录下能见到s3c-sdhci.1,s3c-sdhci.2,s3c-sdhci.3设备节点。 9 if (err) 10 return err; 11 12 led_trigger_register_simple(dev_name(&host->class_dev), &host->led); 13 14 #ifdef CONFIG_DEBUG_FS 15 mmc_add_host_debugfs(host); 16 #endif 17 mmc_host_clk_sysfs_init(host); 18 19 mmc_start_host(host); 20 register_pm_notifier(&host->pm_notify); 21 22 return 0; 23 }
2.5 函数mmc_start_host
定义位于:\drivers\mmc\core\core.c
1 void mmc_start_host(struct mmc_host *host) 2 { 3 host->f_init = max(freqs[0], host->f_min); 4 host->rescan_disable = 0; 5 if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) 6 mmc_power_off(host); 7 else 8 mmc_power_up(host); 9 mmc_detect_change(host, 0);//用来检测SD卡的 10 }
2.5.1 函数mmc_power_off
最关心的的是host->ios当中的内容,前段的赋值真正作用在硬件上是调用host层向上提供的struct mmc_host_ops接口
1 void mmc_power_off(struct mmc_host *host) 2 { 3 if (host->ios.power_mode == MMC_POWER_OFF) 4 return; 5 6 mmc_host_clk_hold(host); 7 8 host->ios.clock = 0; 9 host->ios.vdd = 0; 10 11 12 /* 13 * Reset ocr mask to be the highest possible voltage supported for 14 * this mmc host. This value will be used at next power up. 15 */ 16 host->ocr = 1 << (fls(host->ocr_avail) - 1); 17 18 if (!mmc_host_is_spi(host)) { 19 host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; 20 host->ios.chip_select = MMC_CS_DONTCARE; 21 } 22 host->ios.power_mode = MMC_POWER_OFF; 23 host->ios.bus_width = MMC_BUS_WIDTH_1; 24 host->ios.timing = MMC_TIMING_LEGACY; 25 mmc_set_ios(host); 26 27 /* 28 * Some configurations, such as the 802.11 SDIO card in the OLPC 29 * XO-1.5, require a short delay after poweroff before the card 30 * can be successfully turned on again. 31 */ 32 mmc_delay(1); 33 34 mmc_host_clk_release(host); 35 }
函数mmc_set_ios
1 void mmc_set_ios(struct mmc_host *host) 2 host->ops->set_ios(host, ios); // set_ios 实际上就是 mmc_host_ops 的 .set_ios = msmsdcc_set_ios, 3
2.5.2 函数mmc_detect_change
1 void mmc_detect_change(struct mmc_host *host, unsigned long delay) 2 mmc_schedule_delayed_work(&host->detect, delay); //实际上就是调用我们前面说的延时函数 mmc_rescan
2.5.3 函数mmc_schedule_delayed_work
与之对应的初始化前面已经说过即INIT_DELAYED_WORK(&host->detect, mmc_rescan),延时delay 时间后就会去调用mmc_rescan了。前面我们传递的delay=0,那么这里就没有延时了,直接mmc_rescan,它的功能就是扫描所插入的卡。
1 static int mmc_schedule_delayed_work(struct delayed_work *work, 2 unsigned long delay) 3 { 4 return queue_delayed_work(workqueue, work, delay); 5 }
3 驱动操作函数
1 static struct mmc_host_ops s3cmci_ops = { 2 .request = s3cmci_request, 3 .set_ios = s3cmci_set_ios,//主控制器设置总线和时钟等配置 4 .get_ro = s3cmci_get_ro,//得到只读属性 5 .get_cd = s3cmci_card_present, 6 .enable_sdio_irq = s3cmci_enable_sdio_irq,//开启sdio中断 7 };
3.1 函数s3cmci_request,
它是整个SD主控制器驱动的核心,实现了SD主控制器能与SD卡进行通信的能力。
1 static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq) 2 { 3 struct s3cmci_host *host = mmc_priv(mmc); 4 5 host->status = "mmc request"; 6 host->cmd_is_stop = 0; 7 host->mrq = mrq; 8 9 if (s3cmci_card_present(mmc) == 0) { 10 dbg(host, dbg_err, "%s: no medium present\n", __func__); 11 host->mrq->cmd->error = -ENOMEDIUM; 12 mmc_request_done(mmc, mrq); 13 } else 14 s3cmci_send_request(mmc); 15 }
3.2 函数s3cmci_send_request
1 static void s3cmci_send_request(struct mmc_host *mmc) 2 { 3 struct s3cmci_host *host = mmc_priv(mmc); 4 struct mmc_request *mrq = host->mrq; 5 struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; 6 7 host->ccnt++; 8 prepare_dbgmsg(host, cmd, host->cmd_is_stop); 9 10 /* Clear command, data and fifo status registers 11 Fifo clear only necessary on 2440, but doesn't hurt on 2410 12 */ 13 writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT); 14 writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA); 15 writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA); 16 17 if (cmd->data) { 18 int res = s3cmci_setup_data(host, cmd->data);//实现数据传输 19 20 host->dcnt++; 21 22 if (res) { 23 dbg(host, dbg_err, "setup data error %d\n", res); 24 cmd->error = res; 25 cmd->data->error = res; 26 27 mmc_request_done(mmc, mrq); 28 return; 29 } 30 31 if (s3cmci_host_usedma(host)) 32 res = s3cmci_prepare_dma(host, cmd->data); 33 else 34 res = s3cmci_prepare_pio(host, cmd->data); 35 36 if (res) { 37 dbg(host, dbg_err, "data prepare error %d\n", res); 38 cmd->error = res; 39 cmd->data->error = res; 40 41 mmc_request_done(mmc, mrq); 42 return; 43 } 44 } 45 46 /* Send command */ 47 s3cmci_send_command(host, cmd);//实现命令传输 48 49 /* Enable Interrupt */ 50 s3cmci_enable_irq(host, true); 51 }
首先,SD主控制器由一系列32位寄存器组成。通过软件的方式,即对寄存器赋值,来控制SD主控制器,进而扮演SD主控制器的角色与SD卡取得通信。
3.3 cmdat
根据主控制器的芯片手册,寄存器MMC_CMDAT控制命令和数据的传输,具体内容如下
结合对寄存器MMC_CMDAT的描述,分析代码.:
1 host->cmdat &= ~CMDAT_INIT; // 非初始化状态 2 if (mrq->data) { // 如果存在数据需要传输 3 pxamci_setup_data(host, mrq->data); // 实现主控制器与SD卡之间数据的传输 4 cmdat &= ~CMDAT_BUSY; // 没有忙碌busy信号 5 cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; // 有数据传输,使用DMA 6 if (mrq->data->flags & MMC_DATA_WRITE) 7 cmdat |= CMDAT_WRITE; // 设置为写数据 8 if (mrq->data->flags & MMC_DATA_STREAM) 9 cmdat |= CMDAT_STREAM; // 设置为数据流stream模式
3.4 s3cmci_setup_data
通过DMA实现主控制器与SD卡之间数据的传输.。整个SD卡主控制器设备驱动的实质,通过DMA的方式实现主控制器与SD卡之间数据的读写操作。
1 static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) 2 { 3 u32 dcon, imsk, stoptries = 3; 4 5 /* write DCON register */ 6 7 if (!data) { 8 writel(0, host->base + S3C2410_SDIDCON); 9 return 0; 10 } 11 12 if ((data->blksz & 3) != 0) { 13 /* We cannot deal with unaligned blocks with more than 14 * one block being transferred. */ 15 16 if (data->blocks > 1) { 17 pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz); 18 return -EINVAL; 19 } 20 } 21 22 while (readl(host->base + S3C2410_SDIDSTA) & 23 (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) { 24 25 dbg(host, dbg_err, 26 "mci_setup_data() transfer stillin progress.\n"); 27 28 writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON); 29 s3cmci_reset(host); 30 31 if ((stoptries--) == 0) { 32 dbg_dumpregs(host, "DRF"); 33 return -EINVAL; 34 } 35 } 36 37 dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK; 38 39 if (s3cmci_host_usedma(host)) 40 dcon |= S3C2410_SDIDCON_DMAEN; 41 42 if (host->bus_width == MMC_BUS_WIDTH_4) 43 dcon |= S3C2410_SDIDCON_WIDEBUS; 44 45 if (!(data->flags & MMC_DATA_STREAM)) 46 dcon |= S3C2410_SDIDCON_BLOCKMODE; 47 48 if (data->flags & MMC_DATA_WRITE) { 49 dcon |= S3C2410_SDIDCON_TXAFTERRESP; 50 dcon |= S3C2410_SDIDCON_XFER_TXSTART; 51 } 52 53 if (data->flags & MMC_DATA_READ) { 54 dcon |= S3C2410_SDIDCON_RXAFTERCMD; 55 dcon |= S3C2410_SDIDCON_XFER_RXSTART; 56 } 57 58 if (host->is2440) { 59 dcon |= S3C2440_SDIDCON_DS_WORD; 60 dcon |= S3C2440_SDIDCON_DATSTART; 61 } 62 63 writel(dcon, host->base + S3C2410_SDIDCON); 64 65 /* write BSIZE register */ 66 67 writel(data->blksz, host->base + S3C2410_SDIBSIZE); 68 69 /* add to IMASK register */ 70 imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | 71 S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; 72 73 enable_imask(host, imsk); 74 75 /* write TIMER register */ 76 77 if (host->is2440) { 78 writel(0x007FFFFF, host->base + S3C2410_SDITIMER); 79 } else { 80 writel(0x0000FFFF, host->base + S3C2410_SDITIMER); 81 82 /* FIX: set slow clock to prevent timeouts on read */ 83 if (data->flags & MMC_DATA_READ) 84 writel(0xFF, host->base + S3C2410_SDIPRE); 85 } 86 87 return 0; 88 }
3.5 m3cmci_start_cmd
实现主控制器与SD卡之间指令的传输。
(1)response类型
根据SD卡的协议,当SD卡收到从控制器发来的cmd指令后,SD卡会发出response相应,而response的类型分为R1,R1b,R2,R3,R6,R7,这些类型分别对应不同的指令,各自的数据包结构也不同(具体内容参考SD卡协议)。这里,通过RSP_TYPE对指令cmd的opcode的解析得到相对应的reponse类型,再通过swich赋给寄存器MMC_CMDAT对应的[1:0]位。
(2)将指令和参数写入寄存器
writel()是整个SD卡主控制器设备驱动的实质,通过对主控制器芯片寄存器MMC_CMD,MMC_ARGH,MMC_ARGL,MMC_CMDAT的设置,实现主控制器发送指令到SD卡的功能。
1 static void s3cmci_send_command(struct s3cmci_host *host, 2 struct mmc_command *cmd) 3 { 4 u32 ccon, imsk; 5 6 imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT | 7 S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT | 8 S3C2410_SDIIMSK_RESPONSECRC; 9 10 enable_imask(host, imsk); 11 12 if (cmd->data) 13 host->complete_what = COMPLETION_XFERFINISH_RSPFIN; 14 else if (cmd->flags & MMC_RSP_PRESENT) 15 host->complete_what = COMPLETION_RSPFIN; 16 else 17 host->complete_what = COMPLETION_CMDSENT; 18 19 writel(cmd->arg, host->base + S3C2410_SDICMDARG); 20 21 ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX; 22 ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART; 23 24 if (cmd->flags & MMC_RSP_PRESENT) 25 ccon |= S3C2410_SDICMDCON_WAITRSP; 26 27 if (cmd->flags & MMC_RSP_136) 28 ccon |= S3C2410_SDICMDCON_LONGRSP; 29 30 writel(ccon, host->base + S3C2410_SDICMDCON); 31 }
4 中断
s3cmci_probe(struct platform_device *pdev)中有两个中断,一个为SD主控制器芯片内电路固有的内部中断,另一个为探测引脚的探测到外部有SD卡插拔引起的中断。
4.1 主控芯片内部电路引起的中断
1 if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) { 2 dev_err(&pdev->dev, "failed to request mci interrupt.\n"); 3 ret = -ENOENT; 4 goto probe_iounmap; 5 } 6 7 static irqreturn_t s3cmci_irq(int irq, void *dev_id) 8 { 9 struct s3cmci_host *host = dev_id; 10 ... 11 code详见f:drivers\mmc\host\s3cmci.c 12 }
当调用(*request),即host->ops->request(host, mrq),即上文中的s3cmci_request()后,控制器与SD卡之间开始进行一次指令或数据传输,通信完毕后,主控芯片将产生一个内部中断,以告知此次指令或数据传输完毕。这个中断的具体值将保存在一个名为MMC_I_REG的中断寄存器中以供读取,中断寄存器MMC_I_REG中相关描述如下:
如果中断寄存器MMC_I_REG中的第0位有值,则意味着数据传输完成,执行s3cmci_cmd_done(host, stat);如果中断寄存器MMC_I_REG中的第2位有值,则意味着指令传输完成,执行s3cmci_data_done(host, stat)。
其中stat是从状态寄存器MMC_STAT中读取的值,在代码里主要起到处理错误状态的作用。-> s3cmci_cmd_done 收到结束指令的内部中断信号,主控制器从SD卡那里得到response,结束这次指令传输。这里需要注意,寄存器MMC_RES里已经存放了来自SD卡发送过来的response,以供读取。
4.2 探测引脚引起的中断
中断处理函数中调用mmc_detect_change函数,它将最终调用queue_delayed_work执行工作队列里的mmc_rescan函数。当有SD卡插入或拔出时,硬件主控制器芯片的探测pin脚产生外部中断,进入中断处理函数,执行工作队列里的mmc_rescan,扫描SD总线,对插入或拔出SD卡作相应的处理。mmc_rescan函数见:linux设备驱动-SD卡驱动详解2 core层
1 host->irq_cd = gpio_to_irq(host->pdata->gpio_detect); 2 /*irq_cd是通过GPIO转换得到的中断号,s3cmci_irq_cd便是该中断实现的函数*/ 3 if (host->irq_cd >= 0) { 4 if (request_irq(host->irq_cd, s3cmci_irq_cd, 5 IRQF_TRIGGER_RISING | 6 IRQF_TRIGGER_FALLING, 7 DRIVER_NAME, host)) { 8 ... 9 } 10 11 static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id) 12 { 13 struct s3cmci_host *host = (struct s3cmci_host *)dev_id; 14 15 dbg(host, dbg_irq, "card detect\n"); 16 17 mmc_detect_change(host->mmc, msecs_to_jiffies(500)); 18 19 return IRQ_HANDLED; 20 }
函数mmc_detect_change
1 void mmc_detect_change(struct mmc_host *host, unsigned long delay) 2 { 3 #ifdef CONFIG_MMC_DEBUG 4 unsigned long flags; 5 spin_lock_irqsave(&host->lock, flags); 6 WARN_ON(host->removed); 7 spin_unlock_irqrestore(&host->lock, flags); 8 #endif 9 host->detect_change = 1; 10 mmc_schedule_delayed_work(&host->detect, delay);//调用队列中的mmc_scan函数 11 }
5 sdio host总结
参考博文:https://blog.csdn.net/zqixiao_09/article/details/51039595