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

posted @ 2020-08-19 22:49  Action_er  阅读(1412)  评论(0编辑  收藏  举报