现在的位置: 首页 > 综合 > 正文

MX25上SD卡的插拨检测机制

2017年04月13日 ⁄ 综合 ⁄ 共 5307字 ⁄ 字号 评论关闭

转自:http://blog.csdn.net/armeasy/article/details/6035341

飞思卡尔开发板留有可插拨的SD卡卡槽,BSP包中提供了热插拨的检测机制。在sdhci_probe_slot函数中,gpio_sdhc_active函数初始化SD卡相关的GPIO口,包括SD卡检测脚的初始化。下面的程序实现SD卡检测中断号的申请,以及中断的触发方式:

host->detect_irq = platform_get_irq(pdev, 1);//申请卡检测中断号

         if (!host->detect_irq)

         {

                   host->flags &= ~SDHCI_CD_PRESENT;

                   if ((pdev->id >= 0) && (pdev->id < MXC_SDHCI_NUM))

                            mxc_fix_chips[pdev->id] = chip;

                   goto no_detect_irq;

         }

         do

         {

                   ret = host->plat_data->status(host->mmc->parent);//获取SD卡的存在状态

                   if (ret)//根据当前SD卡存在状态设置卡检测中断触发类型

                            set_irq_type(host->detect_irq, IRQF_TRIGGER_FALLING);

                   else

                            set_irq_type(host->detect_irq, IRQF_TRIGGER_RISING);

         } while (ret != host->plat_data->status(host->mmc->parent));

 

         ret = host->plat_data->status(host->mmc->parent);

程序首先使用platform_get_irq函数申请一个中断号,然后读取SD卡的存在状态,如果SD卡不在卡槽内,则设置为下降沿触发,反之则设置为上升沿触发。这一点需要与硬件接口对应起来,开发板的SD卡硬件电路如下图所示:

SD1_DET脚即为SD卡检测脚,在没有卡存在的情况下,该脚被硬件上拉为高电平,插入卡后,该脚被卡槽直连到地。因此,在没有卡存在时,需要设置为下降沿触发,反之设置为上升沿触发。其实如果CPU支持双边沿触发,可以设置为双边沿触发,然后在中断处理函数中通过检测脚的电平判断是插入还是拨出。

紧接着程序通过下面的函数初始化自旋锁:

spin_lock_init(&host->lock);

使用INIT_WORK函数初始化卡检测函数的工作队列:

INIT_WORK(&host->cd_wq, esdhc_cd_callback);

使用request_irq函数申请卡检测中断:

if (host->detect_irq)//lqm changed

         {

                   ret = request_irq(host->detect_irq, sdhci_cd_irq, 0,pdev->name, host);//响应检测SD卡中断

                   if (ret)//失败

                            goto out4;

         }

sdhci_cd_irq函数为中断顶半部:

static irqreturn_t sdhci_cd_irq(int irq, void *dev_id)

{

         struct sdhci_host *host = dev_id;

         schedule_work(&host->cd_wq);

         return IRQ_HANDLED;

}

当卡检测中断产生后,中断顶半部调用schedule_work函数调度工作队列执行。这里的传入参数host->cd_wq和前面工作队列初始化的第一个传入参数必须一致。工作队列初始化的第二个参数esdhc_cd_callback为卡检测中断的底半部,产生中断后需要做的工作全在这个函数里面。schedule_work执行后该函数马上会执行:

static void esdhc_cd_callback(struct work_struct *work)

{

         unsigned long flags;

         unsigned int cd_status = 0;

         struct sdhci_host *host = container_of(work, struct sdhci_host, cd_wq);

         do

         {

                   if (host->detect_irq == 0)

                            break;

                   cd_status = host->plat_data->status(host->mmc->parent);//读取SD卡状态

                   if (cd_status)//如果卡槽内没有卡,则设置为下降沿触发,否则为上升沿触发

                            set_irq_type(host->detect_irq, IRQF_TRIGGER_FALLING);

                   else

                            set_irq_type(host->detect_irq, IRQF_TRIGGER_RISING);

         } while (cd_status != host->plat_data->status(host->mmc->parent));

         cd_status = host->plat_data->status(host->mmc->parent);//获得卡状态

         printk(KERN_INFO"cd_status=%d %s/n",cd_status, cd_status ? "removed" : "inserted");//lqm added.

         /* If there is no card, call the card detection func immediately. */

         if (!cd_status) // 当没卡时插入卡,开启定时器

         {

                   if (host->flags & SDHCI_CD_TIMEOUT)

                            host->flags &= ~SDHCI_CD_TIMEOUT;

                   else

                   {

                            mod_timer(&host->cd_timer, jiffies + HZ / 4);

                            return;

                   }

         }

         cd_status = host->plat_data->status(host->mmc->parent);

         if (cd_status)//cd_status=1表示没有卡

                   host->flags &= ~SDHCI_CD_PRESENT;

         else

                   host->flags |= SDHCI_CD_PRESENT;

         /* Detect there is a card in slot or not */

         spin_lock_irqsave(&host->lock, flags);//自旋锁第三步:获得自旋锁,保护临界区

         if (!(host->flags & SDHCI_CD_PRESENT)) //当卡拨出时执行下面程序

         {

                   printk(KERN_INFO"%s: Card removed and resetting controller./n",mmc_hostname(host->mmc));

                   if (host->mrq)

                   {

                            struct mmc_data *data;

                            data = host->data;

                            host->data = NULL;

                            printk(KERN_ERR"%s: Card removed during transfer!/n",mmc_hostname(host->mmc));

                            printk(KERN_ERR"%s: Resetting controller./n",mmc_hostname(host->mmc));

                            if ((host->flags & SDHCI_USE_EXTERNAL_DMA) && (data != NULL))

 {

                                     dma_unmap_sg(mmc_dev(host->mmc), data->sg,

                                                    host->dma_len, host->dma_dir);

                                     host->dma_size = 0;

                            }

                            sdhci_reset(host, SDHCI_RESET_CMD);

                            sdhci_reset(host, SDHCI_RESET_DATA);

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

                            tasklet_schedule(&host->finish_tasklet);

                   }

                   if (host->init_flag > 0)

                            host->init_flag--;

                   else

                            sdhci_init(host);

         }

         spin_unlock_irqrestore(&host->lock, flags);//自旋锁第四步:解锁

         if (host->flags & SDHCI_CD_PRESENT)

         {

                   printk(KERN_ERR"SDHCI_CD_PRESENT.../n");// lqm added.

                   del_timer(&host->cd_timer);

                   mmc_detect_change(host->mmc, msecs_to_jiffies(100));// lqm masked for test.

         }

         else

         {

                   printk(KERN_ERR"no SDHCI_CD_PRESENT.../n");

                   mmc_detect_change(host->mmc, 0);

         }

}

    由于卡状态发生了改变,因此函数的第一步即先读取卡状态,根据不同的状态设置对应的卡中断方式。如果是卡插入造成的中断,则打开定时器,进而执行卡挂载等操作,如果是卡拨出造成的中断,则复位SD卡模块。注意在卡拨出执行的代码段添加了自旋锁,正因为它,前面才使用了spin_lock_init函数初始化自旋锁。

抱歉!评论已关闭.