SD卡驅動學習(二)

從上一篇我們已經知道了SD卡涉及到的結構體以及SD的層次結構。現在首先從控制器驅動入手分析,以三星6410爲例。

以平臺驅動的方式註冊控制器驅動

static int __init sdhci_s3c_init(void)

{

         return  platform_driver_register(&sdhci_s3c_driver);

}

 

static void __exit sdhci_s3c_exit(void)

{

         platform_driver_unregister(&sdhci_s3c_driver);

}

module_init(sdhci_s3c_init);

module_exit(sdhci_s3c_exit);

 

static struct platform_driver sdhci_s3c_driver = {

         .probe                = sdhci_s3c_probe,

         .remove            = __devexit_p(sdhci_s3c_remove),

         .suspend  = sdhci_s3c_suspend,

         .resume           = sdhci_s3c_resume,

         .driver                = {

                   .owner     = THIS_MODULE,

                   .name       = "s3c-sdhci",

         },

};

當匹配成功後,進入sdhci_s3c_probe函數

static int __devinit sdhci_s3c_probe(struct platform_device *pdev)

{

         ……..

         struct sdhci_host *host;

         host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c));

         ret = sdhci_add_host(host);//完成SDHCIMMC相關匹配參數以及sdhci_ops的配置

         ……..

}

 

int sdhci_add_host(struct sdhci_host *host)

{

         ………

tasklet_init(&host->card_tasklet,

                   sdhci_tasklet_card, (unsigned long)host);

         tasklet_init(&host->finish_tasklet,

                   sdhci_tasklet_finish, (unsigned long)host);

         ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,

                   mmc_hostname(mmc), host);

         if (ret)

                   goto  untasklet;

         ………

}

在中斷處理例程中,首先讀取SD卡額狀態,根據不同的狀態完成不同的操作

static irqreturn_t sdhci_irq(int irq, void *dev_id)

{

         ………

         intmask = sdhci_readl(host, SDHCI_INT_STATUS);

         if (!intmask || intmask == 0xffffffff) {

                   result = IRQ_NONE;

                   goto out;

         }

         DBG("*** %s got interrupt: 0x%08x\n",

                   mmc_hostname(host->mmc), intmask);

         if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {

                   sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |

                            SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);

                   tasklet_schedule(&host->card_tasklet);

         }

         intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);

         if (intmask & SDHCI_INT_CMD_MASK) {

                   sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,

                            SDHCI_INT_STATUS);

                   sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);

         }

         if (intmask & SDHCI_INT_DATA_MASK) {

                   sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,

                            SDHCI_INT_STATUS);

                   sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);

         }

         intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);

         intmask &= ~SDHCI_INT_ERROR;

         if (intmask & SDHCI_INT_BUS_POWER) {

                   printk(KERN_ERR "%s: Card is consuming too much power!\n",

                            mmc_hostname(host->mmc));

                   sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);

         }

         ……..

}

通過sdhci_cmd_irqsdhci_data_irq發送命令和數據,如果狀態時有SD卡插入或拔出,我們將進入任務調度host->card_tasklet,調度器的初始化在sdhci_add_host

static void sdhci_tasklet_card(unsigned long param)

{

         struct sdhci_host *host;

         unsigned long flags;

         host = (struct sdhci_host*)param;

         spin_lock_irqsave(&host->lock, flags);

         if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {

                   if (host->mrq) {

                            printk(KERN_ERR "%s: Card removed during transfer!\n",

                                     mmc_hostname(host->mmc));

                            printk(KERN_ERR "%s: Resetting controller.\n",

                                     mmc_hostname(host->mmc));

                            sdhci_reset(host, SDHCI_RESET_CMD);

                            sdhci_reset(host, SDHCI_RESET_DATA);

                            host->mrq->cmd->error = -ENOMEDIUM;

                            tasklet_schedule(&host->finish_tasklet);

                   }

         }

         spin_unlock_irqrestore(&host->lock, flags);

         mmc_detect_change(host->mmc, msecs_to_jiffies(200));

}

在任務調度裏面首先還是判斷卡和控制器的狀態,然後調用mmc_detect_change做一個延時操作後調用mmc_schedule_delayed_work

void mmc_detect_change(struct mmc_host *host, unsigned long delay)

{

#ifdef CONFIG_MMC_DEBUG

         unsigned long flags;

         spin_lock_irqsave(&host->lock, flags);

         WARN_ON(host->removed);

         spin_unlock_irqrestore(&host->lock, flags);

#endif

         mmc_schedule_delayed_work(&host->detect, delay);

}

host->detect工作隊列在mmc_alloc_host中初始化

INIT_DELAYED_WORK(&host->detect, mmc_rescan);

當有卡插入的時候,進入控制器中斷處理函數,處理工作隊列,最後調用mmc_rescan,進入core層。

void mmc_rescan(struct work_struct *work)

{

         …….

         struct mmc_host *host =

                   container_of(work, struct mmc_host, detect.work);//得到mmc_host的數據

         if (host->rescan_disable)

                   return;

         mmc_bus_get(host);

         /*

          * if there is a _removable_ card registered, check whether it is

          * still present

          */

         if (host->bus_ops && host->bus_ops->detect && !host->bus_dead

             && !(host->caps & MMC_CAP_NONREMOVABLE))

                   host->bus_ops->detect(host);

         /*

          * Let mmc_bus_put() free the bus/bus_ops if we've found that

          * the card is no longer present.

          */

         mmc_bus_put(host);

         mmc_bus_get(host);

         /* if there still is a card present, stop here */

         if (host->bus_ops != NULL) {

                   mmc_bus_put(host);

                   goto out;

         }

         /*

          * Only we can add a new handler, so it's safe to

          * release the lock here.

          */

         mmc_bus_put(host);

         if (host->ops->get_cd && host->ops->get_cd(host) == 0)

                   goto out;

         mmc_claim_host(host);

         for (i = 0; i < ARRAY_SIZE(freqs); i++) {

                   if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))

                            break;

                   if (freqs[i] < host->f_min)

                            break;

         }

         mmc_release_host(host);

 out:

         if (host->caps & MMC_CAP_NEEDS_POLL)

                   mmc_schedule_delayed_work(&host->detect, HZ);

}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章