MMC/SD的热插拔原理

来源:互联网 发布:精仿阿迪达斯淘宝店 编辑:程序博客网 时间:2024/05/12 02:50

1.MMC/SD的热插拔原理:
1)法1: 设置一个定时器去检查
2)法2: 设置拔插中断检测

2.
//fs2410 sd卡驱动初始化步骤:
pxamci_init()
-> platform_device_register()
-> driver_register()
-> pxamci_probe()
-> // card detect, GPG10 eint18 and enable pull-up => S3C2410_GPGCON 
                S3C2410_GPGUP
   // enable eint18 filter and both edge int  => S3C2410_EXTINT2
   // set SDCLK,SDCMD,SDDAT0~SDDAT3     => S3C2410_GPECON
   // Enable SDI Clock        => S3C2410_CLKCON 1
   // Set INIT clock, about 400KHz     => S3C2410_SDIPRE 1
   // Type B, FIFO reset, clk enable    => S3C2410_SDICON 1
   // Wait 74SDCLK for MMC card      => udelay(200)
   // mask all sdi irq        => S3C2410_SDIIMSK
   //clear all status        => S3C2410_SDICSTA
   -> INIT_WORK(&card_hotplug_task, card_hotplug, dev)  
   -> request_irq(IRQ_EINT18, cd_isr) 
   -> cd_start()  
      ->如果当前没插上卡,则每250ms检测一次是否有插卡动作
    如果当前插上sd卡,则每1s去检测一下是否有拔卡动作
  
8.65.fs2410检测sd卡拔插的原理:
1)硬件上:
GPG10(EINT18)用于SD卡检测:
GPG10=1  //没插入sd卡
GPG10=0  //插入了sd卡

2)软件上:
a)EINT18用于检测sd卡的拔插动作,当有sd卡拔插动作时会产生一个EINT18中断
  (both edge int)而EINT18中断服务程序开始启动sd卡拔插检测定时器cd_func()
b)定时器检测sd卡的插拔动作的过程 : cd_func()
  =>当检测到有sd卡插拔动作就唤醒任务 : card_hotplug() 进行插卡初始化处理 或者
    拔卡注销处理 过程

=> 具体代码实现如下:
static void cd_func(unsigned long data)
{
    u16 r, i;

    r = readl(S3C2410_GPGDAT) & (1 << 10);/*检测出sd卡的当前状态 : 假设为插入*/
    for (i = 0; i < 5; i++)     /*为了防抖动,因此重试5次连续检查
                是否真的为插入动作*/
        if (r != (readl(S3C2410_GPGDAT) & (1 << 10)))
        {
         // 在这瞬间发生了sd相反的插拔事件
            cd_lst = -1;
            mod_timer(&cd_timer, jiffies + HZ / 100); /*10ms后再次检查*/
            return;
        }

 //前面5次检测sd卡拔插状态都一致,则继续往下运行
    r = r ? 0 : 1;
    cd_cnt++;
    if (r != cd_lst)
    {
        cd_lst = r; /*保存好当前的sd卡拔插状态:假设为插入, cd_lst=1*/
        cd_cnt = 0; /*sd卡新状态的第一次检测, 为了防抖动,后面每隔10ms连续检测
                      20次以确认真的是插卡动作*/
    }

    if (cd_cnt < 20)
    {
        mod_timer(&cd_timer, jiffies + HZ / 100); /*在200ms内检测了20次*/
    }
    else
    { /*每隔10ms连续检测20次状态都一致,确实是插卡动作,因此停止掉定时器,唤醒
     任务开始sd卡初始化动作并再次使能sd卡检测中断EINT18*/
        del_timer_sync(&cd_timer);
        if (cd_sta != r)
        {
            cd_sta = r;
            schedule_work(&card_hotplug_task);
        }
        enable_irq(IRQ_EINT18);
    }

 
c)
static u8 cd_sta; // 1:插入sd卡, 0:拔下sd卡
static u8 cd_lst; // 保存前一次的sd卡插入状态
static u16 cd_cnt;

cd_sta的改变位置和条件:
cd_sta仅在函数cd_func()里改变状态

d)card_hotplug()被唤醒后,其初始化过程如下:
//fs2410插入sd卡的初始化过程:
card_hotplug()
-> card_insert()
-> to_platform_device()   // 获得平台相关的sd卡控制器资源(IO/IRQ资源)
-> platform_device_resource() // 获得IO资源
-> platform_device_irq()  // 获得IRQ资源
-> mmc_alloc_host()    
   -> //初始化结构pxamci_host
   -> INIT_WORK(&host->detect, mmc_rescan, host);
-> pxamci_stop_clock()   // 停止sd卡时钟
-> request_irq(IRQ_SDI, pxamci_irq)
-> request_irq(IRQ_DMA0, pxamci_dma_irq)
-> dev_set_drvdata()
-> mmc_add_host()
   -> mmc_power_off()
   -> mmc_detect_change()
   -> 唤醒挂在任务队列host->detect 上的任务mmc_rescan()
      schedule_work(&host->detect) 

mmc_alloc_host()  
   -> INIT_WORK(&host->detect, mmc_rescan, host);
      -> mmc_add_host()/schedule_work(&host->detect)
      -> kthread()
      -> worker_thread()
      -> mmc_rescan()  
-> mmc_rescan()
   -> mmc_claim_host()
   -> mmc_setup() 
      -> mmc_power_up()   // 这里讨论第一次初始化刚插入sd卡的流程
         -> host->ops->set_ios()
            pxamci_set_ios() // 设置bus_mode = MMC_BUSMODE_OPENDRAIN,
                 // power_mode = MMC_POWER_UP
         -> mmc_delay(1);
            clock = host->f_min
            power_mode = MMC_POWER_ON
         -> mmc_delay(2) 
         
      -> mmc_send_op_cond()  
      -> mmc_select_voltage()
      -> mmc_send_op_cond()  
      -> mmc_discover_cards() 
         -> mmc_decode_cid()   
      -> mmc_read_csds()  
         -> mmc_decode_csd() 
   -> list_empty()    
   -> mmc_release_host(host)                       
   -> list_for_each_safe()
   -> mmc_list_to_card()  
   -> mmc_card_present() & mmc_card_dead() 
   -> mmc_register_card()  
      -> device_add()
         -> bus_add_device()
         -> device_attach()
         -> bus_match()
         -> mmc_drv_probe()
         -> drv->probe()
            mmc_blk_probe()  
     -> add_disk()   
        -> register_disk()
           -> check_partition() 
        -> blk_register_queue()       
      -> device_create_file()
       
kthread()
-> worker_thread()
-> mmc_rescan()
-> mmc_register_card()
-> device_add()
-> bus_add_device()
-> device_attach()
-> bus_match()
-> mmc_blk_probe()

pxamci_request()
-> pxamci_setup_data() 
-> pxamci_start_cmd() 

static struct resource s3c2410_sdi_resources[] =
{
    [0] =
    {
    .start = S3C2410_VA_SDI,
    .end = S3C2410_VA_SDI + 0x100 - 1,
    .flags = IORESOURCE_MEM,
    },
   
    [1] =
    {
    .start = IRQ_SDI,
    .end = IRQ_SDI,
    .flags = IORESOURCE_IRQ,
    },
};

3.Linux 块设备io请求的处理步骤:
 1.每个块驱动程序至少拥有一个 I/O 请求队列。在任意给定时刻,该队列包含了内核
   想在该驱动程序的设备上完成的所有 I/0 操作。
 1)每个设备有一个默认使用的请求队列
 2)块驱动程序中最重要的函数就是request 函数,该函数执行数据读写相关的底层操作
 1.1
 而每一个MMC卡是看作几个分区来使用的,表征一张MMC卡设备的一个分区的信息放在
 结构:hd_struct
 
 2.关于MMC卡块设备的请求队列:
 1)请求队列初始化在 : mmc_media.c/mmc_media_init()/
   //blk_init_queue(BLK_DEFAULT_QUEUE(mmc_major), DEVICE_REQUEST); Line487
   #define DEVICE_REQUEST      mmc_media_request
   故MMC卡的请求队列处理函数为:mmc_media_request()

 

4.fs2410mmc卡驱动注册为块设备的步骤/初始化IO请求队列的步骤/io请求的处理步骤:
当要读取块设备(这里以sd卡为例)上的数据时,会调用到__bread()函数, 然后linux内核
会向sd卡设备产生一个io请求.因此块设备操作函数__generic_unplug_device()就调用到
sd卡驱动提供的io请求处理函数mmc_request(),mmc_request()唤醒等在等待队列
md->thread_wq 上的任务 -> mmc_queue_thread(),而mmc_queue_thread()会调用
mq->issue_fn()即mmc_blk_issue_rq(),从而开始一整个io请求的处理过程
mmc_blk_issue_rq(),如下所示:
=>如下所示:
mmc_request()  
 -> req = elv_next_request(q) 
mmc_blk_issue_rq()   
-> mmc_card_claim_host() 
   ->
      DECLARE_WAITQUEUE(wait, current); 
      add_wait_queue(&host->wq, &wait); 
            
   -> mmc_wait_for_cmd() 
-> MMC_BLK_RQ_LED_START() // for test
-> mmc_wait_for_req()  
   -> mmc_start_request()
      -> .request()
  pxamci_request()  
  -> pxamci_stop_clock() // why? /*clock type is MMC type?*/
  -> pxamci_start_cmd()
     -> writel(0x1e00, S3C2410_SDICSTA)   //clear cmd status
     -> writel(host->clkrt, S3C2410_SDIPRE)  //set clock rate
     -> writel(FIFO_TYPE | 1, S3C2410_SDICON); //Type A, enable clock
     -> pxamci_enable_irq()
-> MMC_BLK_RQ_LED_STOP() // for test
-> mmc_card_release_host() // Release a MMC host, allowing others to claim the
       // host for their operations.
   -> host->card_busy = NULL
   -> wake_up(&host->wq) 

mmc_blk_init()
-> register_blkdev()
-> devfs_mk_dir()
-> mmc_register_driver(&mmc_driver)
-> mmc_blk_probe()
   -> mmc_blk_alloc()     
      -> find_first_zero_bit()
      -> kmalloc(sizeof(struct mmc_blk_data))
      -> alloc_disk()
      -> mmc_init_queue()
         -> blk_init_queue(mmc_request) 
        -> blk_queue_prep_rq(mmc_prep_request)
         -> blk_queue_bounce_limit()
        -> init_waitqueue_head(&mq->thread_wq)
         -> kernel_thread(mmc_queue_thread)
         -> init_completion()
      -> blk_queue_max_sectors()
      -> blk_queue_hardsect_size()
      -> set_capacity()
   -> mmc_blk_set_blksize()
   -> mmc_set_drvdata()
   -> add_disk(md->disk) 

5.MMC卡的存储介质组织:
寻址的单位为字节
每512字节组成一个块,称为扇区。每个扇区可以独立的读取,写入,和擦除。

6 SD/mmc卡的挂载.
mount -t vfat /dev/mmcblk0 /tmp     // ok
mount -t vfat -o codepage=936,iocharset=cp936 /dev/mmcblk0 /tmp  // ok,支持简体中文  

原创粉丝点击