mini2440 sd卡加载过程详解
生活随笔
收集整理的這篇文章主要介紹了
mini2440 sd卡加载过程详解
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最近在研究wifi模組, 是sdio接口的, 而手頭剛好有一塊mini2440,了解了一下sdio加載的過程, 發現和sd卡加載的過程是類似的。?
這里用mini2440的內核源碼, 來剖析一下sd卡的加載過程:
首先, mini2440內核加載的時候, 就會指定一部分設備初始化列表:
//---------------------------- arch/arm/mach-s3c2440/mach-mini2440.c ----------------------------// /* devices we initialise */ static struct platform_device *mini2440_devices[] __initdata = {&s3c_device_usb,&s3c_device_rtc,&s3c_device_lcd,&s3c_device_wdt,&s3c_device_i2c0,&s3c_device_iis,&mini2440_device_eth,&s3c24xx_uda134x,&s3c_device_nand,&s3c_device_sdi, // sd設備&s3c_device_usbgadget, }; /* MMC/SD */ static struct s3c24xx_mci_pdata mini2440_mmc_cfg = {.gpio_detect = S3C2410_GPG(8), // 插入檢測引腳.gpio_wprotect = S3C2410_GPH(8), // 寫保護引腳.set_power = NULL,.ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34, // 有效電壓范圍是3.2~3.3V, 3.3~3.4V };static void __init mini2440_machine_init(void) {s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg; platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices)); }來看看s3c_device_sdi的定義: //---------------------------- arch/arm/plat-s3c24xx/devs.c ----------------------------// /* SDI */ static struct resource s3c_sdi_resource[] = {[0] = {.start = S3C24XX_PA_SDI,.end = S3C24XX_PA_SDI + S3C24XX_SZ_SDI - 1,.flags = IORESOURCE_MEM,},[1] = {.start = IRQ_SDI,.end = IRQ_SDI,.flags = IORESOURCE_IRQ,}};struct platform_device s3c_device_sdi = {.name = "s3c2410-sdi",.id = -1,.num_resources = ARRAY_SIZE(s3c_sdi_resource),.resource = s3c_sdi_resource, };有些人會奇怪, 我們這里不是在講解2440嗎, 為什么這里是s3c2410-sdi, 別急, 往下看: //---------------------------- arch/arm/mach-s3c2440/mach-mini2440.c ----------------------------// static void __init mini2440_machine_init(void) {s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg; platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices)); }//---------------------------- arch/arm/plat-s3c24xx/s3c244x.c ----------------------------// void __init s3c244x_map_io(void) {iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc));/* rename any peripherals used differing from the s3c2410 */s3c_device_sdi.name = "s3c2440-sdi"; // 重命名為s3c2440-sdis3c_device_i2c0.name = "s3c2440-i2c";s3c_device_nand.name = "s3c2440-nand";s3c_device_usbgadget.name = "s3c2440-usbgadget"; }理解了吧? ?接下來講sd驅動的加載:
//---------------------------- drivers/mmc/host/s3cmci.c ----------------------------// static struct platform_device_id s3cmci_driver_ids[] = {{.name = "s3c2410-sdi",.driver_data = 0,}, {.name = "s3c2412-sdi",.driver_data = 1,}, {.name = "s3c2440-sdi",.driver_data = 1, // 下面會用到!!!},{ } };static struct platform_driver s3cmci_driver = {.driver = {.name = "s3c-sdi",.owner = THIS_MODULE,.pm = s3cmci_pm_ops,},.id_table = s3cmci_driver_ids,.probe = s3cmci_probe,.remove = __devexit_p(s3cmci_remove),.shutdown = s3cmci_shutdown, };static int __init s3cmci_init(void) {return platform_driver_register(&s3cmci_driver); }module_init(s3cmci_init); 驅動和設備匹配上的話,就會調用驅動的probe函數: static int __devinit s3cmci_probe(struct platform_device *pdev) {struct s3cmci_host *host;struct mmc_host *mmc;is2440 = platform_get_device_id(pdev)->driver_data; // 是否是2440, 這里就用到上面提到的driver_data,mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); // 分配mmc_host結構體for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) { // SDCLK SDCMD DATA0~DATA3ret = gpio_request(i, dev_name(&pdev->dev)); // 申請gpio資源if (ret) {dev_err(&pdev->dev, "failed to get gpio %d\n", i);for (i--; i >= S3C2410_GPE(5); i--)gpio_free(i);goto probe_free_host;}}host = mmc_priv(mmc); // 獲取private域host->mmc = mmc;host->pdev = pdev;host->is2440 = is2440;host->pdata = pdev->dev.platform_data; // 即上面提到的mini2440_mmc_cfgif (!host->pdata) {pdev->dev.platform_data = &s3cmci_def_pdata;host->pdata = &s3cmci_def_pdata;}if (is2440) {host->sdiimsk = S3C2440_SDIIMSK;host->sdidata = S3C2440_SDIDATA;host->clk_div = 1;} else {host->sdiimsk = S3C2410_SDIIMSK;host->sdidata = S3C2410_SDIDATA;host->clk_div = 2;}// 申請內存host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 這里會讀取IORESOURCE_MEM域host->mem = request_mem_region(host->mem->start,resource_size(host->mem), pdev->name);host->base = ioremap(host->mem->start, resource_size(host->mem));host->irq = platform_get_irq(pdev, 0); // 這里會讀取IORESOURCE_IRQ域, 返回start地址if (host->irq == 0) {dev_err(&pdev->dev, "failed to get interrupt resouce.\n");ret = -EINVAL;goto probe_iounmap;}// 申請中斷,該中斷為讀寫SD卡數據時所產生的中斷if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {dev_err(&pdev->dev, "failed to request mci interrupt.\n");ret = -ENOENT;goto probe_iounmap;}// 禁止上面所申請的中斷disable_irq(host->irq); host->irq_state = false;if (!host->pdata->no_detect) { // 如果SD控制器具有檢測SD卡插拔狀態的功能ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect"); // 申請插入檢測iohost->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);if (host->irq_cd >= 0) {if (request_irq(host->irq_cd, s3cmci_irq_cd, // 注冊插入檢測中斷處理函數, 申請中斷,當有SD卡插入或拔出時,則進入該中斷IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING,DRIVER_NAME, host)) {dev_err(&pdev->dev,"can't get card detect irq.\n");ret = -ENOENT;goto probe_free_gpio_cd;}} else {dev_warn(&pdev->dev,"host detect has no irq available\n");gpio_direction_input(host->pdata->gpio_detect);}}if (!host->pdata->no_wprotect) { // 如果SD控制器具有寫保護的功能ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp"); // 申請寫保護ioif (ret) {dev_err(&pdev->dev, "failed to get writeprotect\n");goto probe_free_irq_cd;}gpio_direction_input(host->pdata->gpio_wprotect);}if (s3cmci_host_usedma(host)) { // 如果使用dma...}host->clk = clk_get(&pdev->dev, "sdi");ret = clk_enable(host->clk); // 使能clkhost->clk_rate = clk_get_rate(host->clk); // 通過時鐘信號源獲取時鐘頻率mmc->ops = &s3cmci_ops;mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; // 默認是支持3.2~3.3V和3.3~3.4V #ifdef CONFIG_MMC_S3C_HW_SDIO_IRQmmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; #elsemmc->caps = MMC_CAP_4_BIT_DATA; #endifmmc->f_min = host->clk_rate / (host->clk_div * 256);mmc->f_max = host->clk_rate / host->clk_div;if (host->pdata->ocr_avail)mmc->ocr_avail = host->pdata->ocr_avail; // 如果用戶自定義了ocr_avail,即支持的工作電壓,則使用自定義mmc->max_blk_count = 4095; // 一次請求的最大block數mmc->max_blk_size = 4095; // block的最大值mmc->max_req_size = 4095 * 512; // 一次請求的最大字節數mmc->max_seg_size = mmc->max_req_size; // 最大塊大小mmc->max_phys_segs = 128;mmc->max_hw_segs = 128;ret = s3cmci_cpufreq_register(host);ret = mmc_add_host(mmc); // 將mmc_host添加進系統s3cmci_debugfs_attach(host); platform_set_drvdata(pdev, mmc); }其中: static struct mmc_host_ops s3cmci_ops = {.request = s3cmci_request, // 實現命令和數據的傳輸.set_ios = s3cmci_set_ios, // 設置硬件的IO, 包括工作電壓等 .get_ro = s3cmci_get_ro, // 讀取寫保護的狀態.get_cd = s3cmci_card_present, // 獲取卡是否還存在的狀態.enable_sdio_irq = s3cmci_enable_sdio_irq, };從對s3cmci_probe函數的分析可以看出,該函數除了對s3cmci_host和mmc_host這兩個結構體賦值外,最重要的工作就是調用了mmc_alloc_host函數和mmc_add_host函數。前者分配一個mmc_host,后者添加一個mmc_host。
下面我們就來介紹這兩個函數,這兩個函數都在drivers/mmc/core/host.c文件內。
//---------------------------- drivers/mmc/core/host.c ----------------------------// struct mmc_host *mmc_alloc_host(int extra, struct device *dev) {host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);err = idr_get_new(&mmc_host_idr, host, &host->index); // 分配新的idr入口dev_set_name(&host->class_dev, "mmc%d", host->index); // 設置設備的名字host->parent = dev;host->class_dev.parent = dev;host->class_dev.class = &mmc_host_class;// 初始化設備結構體device_initialize(&host->class_dev);spin_lock_init(&host->lock);init_waitqueue_head(&host->wq); // 初始化等待隊列,工作隊列INIT_DELAYED_WORK(&host->detect, mmc_rescan); // 添加到等待隊列INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);/** By default, hosts do not support SGIO or large requests.* They have to set these according to their abilities.*/host->max_hw_segs = 1;host->max_phys_segs = 1;host->max_seg_size = PAGE_CACHE_SIZE;host->max_req_size = PAGE_CACHE_SIZE;host->max_blk_size = 512;host->max_blk_count = PAGE_CACHE_SIZE / 512;return host; }int mmc_add_host(struct mmc_host *host) {...err = device_add(&host->class_dev);mmc_start_host(host);... }在mmc_add_host函數內,調用mmc_start_host函數來啟動SD: //---------------------------- drivers/mmc/core/core.c ----------------------------// void mmc_start_host(struct mmc_host *host) {mmc_power_off(host);mmc_detect_change(host, 0); }static void mmc_power_off(struct mmc_host *host) {host->ios.clock = 0;host->ios.vdd = 0;if (!mmc_host_is_spi(host)) {host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;host->ios.chip_select = MMC_CS_DONTCARE;}host->ios.power_mode = MMC_POWER_OFF;host->ios.bus_width = MMC_BUS_WIDTH_1; // 單線模式host->ios.timing = MMC_TIMING_LEGACY;mmc_set_ios(host); // 設置工作電壓和模式 }void mmc_detect_change(struct mmc_host *host, unsigned long delay) {mmc_schedule_delayed_work(&host->detect, delay); }static int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) {return queue_delayed_work(workqueue, work, delay); }在mmc_start_host函數內調用mmc_detect_change函數來檢測是否有SD卡已經插入。mmc_detect_change函數又調用mmc_schedule_delayed_work函數,mmc_schedule_delayed_work函數又調用了queue_delayed_work函數。
這樣經過一段延時后,會執行在mmc_alloc_host函數中定義的工作隊列(INIT_DELAYED_WORK(&host->detect,mmc_rescan);)所指向的mmc_rescan函數。
為什么要延時,這和按鍵去抖延時相類似。
下面我們來介紹mmc_rescan函數,它用來具體執行掃描并檢測是否有SD卡插入的:
//---------------------------- drivers/mmc/core/sd.c ----------------------------// /** Starting point for SD card init.*/ int mmc_attach_sd(struct mmc_host *host) {int err;u32 ocr, rocr;err = mmc_send_app_op_cond(host, 0, &ocr);if (err)return err;mmc_sd_attach_bus_ops(host);if (host->ocr_avail_sd)host->ocr_avail = host->ocr_avail_sd;/** We need to get OCR a different way for SPI.*/if (mmc_host_is_spi(host)) {mmc_go_idle(host);err = mmc_spi_read_ocr(host, 0, &ocr);if (err)goto err;}// 設置電壓,最終調用的是s3cmci_set_ios函數rocr = mmc_select_voltage(host, ocr);/** Can we support the voltage(s) of the card(s)?*/if (!rocr) {err = -EINVAL;goto err;}/* 檢測和初始化卡* Detect and init the card.* 在mmc_sd_init_card函數內,會通過發送一系列命令來啟動SD卡,并得到寄存器CID,CSD,SCR,RCA等的數據。*/err = mmc_sd_init_card(host, rocr, NULL);if (err)goto err;mmc_release_host(host);/* 添加卡, 它通過調用device_add函數把mmc_card設備添加進系統中,并通知mmc塊設備驅動。* First add the card to the driver model...*/err = mmc_add_card(host->card);mmc_claim_host(host);if (err)goto remove_card;return 0;remove_card:mmc_release_host(host);mmc_remove_card(host->card);host->card = NULL;mmc_claim_host(host); err:mmc_detach_bus(host);pr_err("%s: error %d whilst initialising SD card\n",mmc_hostname(host), err);return err; }static void mmc_sd_attach_bus_ops(struct mmc_host *host) {const struct mmc_bus_ops *bus_ops;if (!mmc_card_is_removable(host))bus_ops = &mmc_sd_ops_unsafe;elsebus_ops = &mmc_sd_ops;mmc_attach_bus(host, bus_ops); }static const struct mmc_bus_ops mmc_sd_ops = {.remove = mmc_sd_remove, // 拔出sd卡的操作.detect = mmc_sd_detect, // 探測sd卡的操作.suspend = NULL,.resume = NULL,.power_restore = mmc_sd_power_restore,.alive = mmc_sd_alive,.shutdown = mmc_sd_suspend, }; 至此,mmc子系統的啟動及檢測SD卡設備就完成了。這個SD卡的檢測是在上電之前就已插入插槽的,那么如果上電以后再把SD卡插入插槽中,系統又是如何檢測的呢?
在前面介紹s3cmci_probe函數的時候,申請了兩個中斷,一個用于數據傳輸,另一個就是用于檢測SD卡的。也就是當SD卡插入時,會引起相關引腳的電平變化,從而觸發該中斷,進入中斷處理函數s3cmci_irq_cd:
//---------------------------- drivers\mmc\host/s3cmci.c ----------------------------// static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id) {struct s3cmci_host *host = (struct s3cmci_host *)dev_id;dbg(host, dbg_irq, "card detect\n");mmc_detect_change(host->mmc, msecs_to_jiffies(500));return IRQ_HANDLED; } mmc_detect_change函數前面介紹過,它是用來檢測是否存在SD卡。它的第二個參數表示延時的時間。在這里一定是要有延時的。
調用mmc_detect_change函數后所執行的內容與前面介紹的一樣,這里就不再重復了。
?
無論是上電之前還是上電之后,SD卡的插入檢測我們在上面都詳細介紹完了,那么SD卡的拔出是如何檢測的呢?當然一定是帶電狀態下才能檢測SD卡的拔出的。
是回到mmc_rescan函數中:
void mmc_rescan(struct work_struct *work) {...struct mmc_host *host = container_of(work, struct mmc_host, detect.work);/* if there is a card registered, check whether it is still present */if ((host->bus_ops != NULL) && host->bus_ops->detect && !host->bus_dead)host->bus_ops->detect(host); // 如果已經注冊過該卡,則檢測卡是否還在... } 上面的語句就是用來檢測SD卡的拔出的。我們在前面也已經介紹過了,在mmc_attach_bus函數內,把mmc_sd_ops賦給了host->bus_ops,
所以host->bus_ops->detect(host);實際回調的是mmc_sd_detect函數。
這里可能會有一個疑問:mmc_attach_bus函數是在mmc_rescan函數之后被調用的,那么就是說在mmc_rescan函數內,mmc_host->bus_ops還沒有被賦值,
所以就不會回調mmc_sd_detect函數了。
我們要注意一點的是,mmc_rescan函數是在上電初始化過程中會被執行一次,因此在初始化的過程中完成了mmc_host->bus_ops的賦值。
而檢測SD卡的拔出一定是在完成初始化以后,這時mmc_host->bus_ops已經被賦值,所以執行mmc_rescan函數中的
host->bus_ops->detect(host);語句不會出現任何問題。
我們來看看回調函數mmc_sd_detect,它定義在drivers/mmc/core/sd.c文件內:
/** Card detection callback from host.*/ static void mmc_sd_detect(struct mmc_host *host) {int err = 0; #ifdef CONFIG_MMC_PARANOID_SD_INITint retries = 5; #endifBUG_ON(!host);BUG_ON(!host->card);mmc_get_card(host->card);/** Just check if our card has been removed.*/ #ifdef CONFIG_MMC_PARANOID_SD_INITwhile(retries) {err = mmc_send_status(host->card, NULL);if (err) {retries--;udelay(5);continue;}break;}if (!retries) {printk(KERN_ERR "%s(%s): Unable to re-detect card (%d)\n",__func__, mmc_hostname(host), err);} #elseerr = _mmc_detect_card_removed(host); #endifmmc_put_card(host->card);if (err) {mmc_sd_remove(host);mmc_claim_host(host);mmc_detach_bus(host);mmc_power_off(host);mmc_release_host(host);} }為什么要用給SD卡發送狀態信息來判斷SD卡是否存活(即是否被拔出)?因為當把SD卡拔出時,當然是得不到任何SD卡的信息,所以會出錯。我們再回到mmc_sdio_detect函數內,如果SD卡已拔出,mmc_select_card函數返回非零值,則執行下面的if里的內容。
下面我們來驗證一下SD驅動。把SD卡插入開發板的插槽中,上電啟動開發板,如果系統能夠打印出類似下面的信息,則說明開發板能夠識別出SD卡:
s3c-sdi s3c2440-sdi: running at 0kHz (requested: 0kHz). s3c-sdi s3c2440-sdi: running at 398kHz (requested: 400kHz). s3c-sdi s3c2440-sdi: running at 398kHz (requested: 400kHz). s3c-sdi s3c2440-sdi: running at 398kHz (requested: 400kHz). s3c-sdi s3c2440-sdi: running at 398kHz (requested: 400kHz). s3c-sdi s3c2440-sdi: running at 398kHz (requested: 400kHz). s3c-sdi s3c2440-sdi: running at 398kHz (requested: 400kHz). s3c-sdi s3c2440-sdi: running at 398kHz (requested: 400kHz). s3c-sdi s3c2440-sdi: running at 16875kHz (requested: 25000kHz). s3c-sdi s3c2440-sdi: running at 16875kHz (requested: 25000kHz). mmc0: new SDHC card at address aaaa mmcblk0: mmc0:aaaa SS08G 7.40 GiB mmcblk0: p1如果先把開發板上電,然后再把SD卡插入插槽中,則系統也會自動打印出上面的信息。如果我們在帶電的情況下把SD卡拔出,則系統會自動打印出類似下面的信息:
mmc0: card aaaa removed s3c-sdis3c2440-sdi: powered down.總結
以上是生活随笔為你收集整理的mini2440 sd卡加载过程详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: W5500+DHCP+DNS+MQTT
- 下一篇: ACC自适应巡航和定速巡航的区别