精华内容
下载资源
问答
  • linux 内核SD卡驱动源代码
  • 主要介绍了详解linux 驱动编写(sd卡驱动),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • SD卡驱动程序

    2018-09-27 08:28:15
    这个SD卡驱动程序是利用了AVR单片机的硬件SPI接口与SD卡通信。测试下来还比较稳定靠谱。
  • 华硕ASUS K43SD网卡驱动程序,本次小编就为大家带来其网卡驱动的官方最新版,有需要的那就下载吧。网卡参数网卡芯片:Atheros AR8151 PCI-E Gigabit Ethernet ControllerWINXP驱动版本:03/02/2012,2.0.11.15WIN7...
  • 兼容FatFs库的STM32F4xx具有DMA功能的SDIO SD卡驱动程序 这是原始STMicroelectronics SDIO驱动程序的修改版本,具有STM32F4xx系列芯片上的即开即用的DMA模式。 某些板卡没有CD引脚(卡检测),因此您应在sdio_sd.c...
  • Micro SD卡驱动程序

    2014-11-07 02:51:34
    在优龙KL26开发板上,实现Micro SD卡驱动代码,不包括文件系统。
  • 近年来,基于FPGA的软核处理器以其高度的设计灵活性和低成本在嵌入式市场中得到重视并不断发展。其中具有代表性的软核处理器有Ahera的 NiosⅡ处理器和Xilinx的MicroBlaze处理器。NiosⅡ处理器具有完全的...此外,SD...
  • 索尼 ex280 广播级高清数码摄像机 SxS存储卡驱动(FAT&UDF;)
  • SD卡驱动是SDHC驱动card_reader_driver_v2.0.0.8,它包含:WindowsXP-KB923293-v3-x86-CHS和WindowsXP-KB934428-x86-CHS4G高速SD卡(SDHC),笔记本自带的读卡器居然不能识别(只能识别普通的低速SD卡),下载了这个...
  • 由于SD卡读/写速度快、移动灵活性好、安全性强,加之体积很小,SD卡被广泛使用于嵌入式便携移动装置上作为嵌入式系统的数据存储设备,如:数码相机、PDA和多媒体播放器等。SD卡用于ReWorks嵌入式系统,必须要开发...
  • sd卡驱动框架.pdf

    2021-07-29 09:14:49
    高清sd卡驱动框架思维导图pdf
  • SD卡驱动程序解析

    千次阅读 2019-06-15 13:56:58
    一、开发环境 主 机:VMWare--Fedora 9 开发板:Mini2440--64MB Nand, Kernel:2.6.30.4 编译器:arm-linux-...MMC:(Multi Media Card)由西门子公司和首推CF的SanDisk于1997年推出的多媒体记忆标准。 SD:(S...

    一、开发环境

    • 主  机:VMWare--Fedora 9
    • 开发板:Mini2440--64MB Nand, Kernel:2.6.30.4
    • 编译器:arm-linux-gcc-4.3.2

    二、MMC/SD介绍及SDI主机控制器

      首先我们来理清几个概念:

    1. MMC:(Multi Media Card)由西门子公司和首推CF的SanDisk于1997年推出的多媒体记忆卡标准。
    2. SD:(Secure Digital Memory Card)由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制的新一代记忆卡标准,已完全兼容MMC标准。
    3. SDIO:(Secure Digital Input and Output Card)安全数字输入输出卡。SDIO是在SD标准上定义了一种外设接口,通过SD的I/O接脚来连接外围设备,并且通过SD上的 I/O数据接位与这些外围设备进行数据传输。是目前较热门的技术,如下图中的一些设备:GPS、相机、Wi-Fi、调频广播、条形码读卡器、蓝牙等。
    4. 工作模式:工作模式是针对主机控制器来说的。也就是说,S3C2440中的SDI控制器可以在符合MMC的标准下工作,或者可以在符合SD的标准下工作,或者可以在符合SDIO的标准下工作。故就分别简称为:MMC模式、SD模式和SDIO模式。
    5. 传输模式:传输模式也是针对主机控制器来说的,指控制器与卡之间数据的传输模式,或者说是总线类型。S3C2440中的SDI控制器可支持SPI、1位和4位的三种传输模式(总线类型)。那么什么又是SPI呢?请参考这里:SPI协议简介;至于1位和4位又是什么意思呢?他们是指传输数据总线的线宽,具体参考数据手册。

      下面使用表格列出了MMC、SD、SDIO的电气特性及性能和不同工作模式下支持的传输模式情况:


      那么,我们现在怎样让主机控制器在我们所要求的工作模式和传输模式上工作呢?很简单,就是对主机控制器的各个寄存器进行相应的配置即可。下面来简单介绍一下SDI主机控制器的结构和各寄存器的用途。

    S3C2440内的SDI主机控制器结构图如下:

      如上图所示,SDI主机控制器是使用1个串行时钟线与5条数据线同步进行信息移位和采样。传输频率通过设定SDIPRE寄存器的相应位的设定来控制,可以修改频率来调节波特率数据寄存器的值。

    各主要寄存器介绍,对于具体的寄存器位的设置就参考数据手册:

    1. SDICON:控制寄存器,完成SD卡基础配置,包括大小端,中断允许,模式选择,时钟使能等。
    2. SDIPRE:波特率预定标器寄存器,对SDCLK的配置。
    3. SDICmdArg:指令参数寄存器,指令的参数存放在这里。
    4. SDICCON:控制指令形式的寄存器,配置SPI还是SDI指令,指令的反馈长度,是否等待反馈,是否运行指令,指令的索引等。
    5. SDICmdSta:指令状态寄存器,指令是否超时,传送,结束,CRC是否正确等。
    6. SDIRSP0-3:反映SD的状态。
    7. SDIDTimer:设置超时时间。
    8. SDIBSize:模块大小寄存器。
    9. SDIDatCon:数据控制寄存器,配置是几线传输,数据发送方向,数据传送方式等。
    10. SDIDatSta:数据状态寄存器,数据是否发送完,CRC效验,超时等。
    11. SDIFSTA:FIFO状态寄存器,DMA传输是否判断FIFO。
    12. SDIIntMsk:中断屏蔽寄存器。
    13. SDIDAT:SDI数据寄存器。

    SDI主机控制器在SD/MMC工作模式下的设置步骤:(注意:因为SD模式兼容MMC模式,所以我们只需了解SD模式的即可,而SDIO的工作模式则是针对SDIO设备的,所以这里就不讨论了)

    1. 设置SDICON寄存器来配置适当的时钟及中断使能;
    2. 设置SDIPRE寄存器适当的值;
    3. 等待74个SDCLK时钟以初始化卡;
    4. 命令操作步骤:
      a. 写命令参数32位到SDICmdArg寄存器;
      b. 设置命令类型并通过设置SDICCON寄存器开始命令传输;
      c. 当SDICSTA寄存器的特殊标志被置位,确认命令操作完成;
      d. 如果命令类型相应,标志是RspFin,否则标志是CmdSend;
      e. 通过对相应位写1,清除SDICmdSta的标志。
    5. 数据操作步骤:
      a. 写数据超时时间到SDIDTimer寄存器;
      b. 写模块大小到SDIBSize寄存器(通常是0x80字节);
      c. 确定模块模式、总线线宽、DMA等且通过设置SDIDatCon寄存器开始数据传输;
      d. 发送数据->写数据到SDIDAT寄存器,当发送FIFO有效(TFDET置位),或一半(TFHalf置位),或空(TFEmpty置位);
      e. 接收数据->从数据寄存器SDIDAT读数据,当接收FIFO有效(RFDET置位),或满(RFFull置位),或一半(RFHalf置位),或准备最后数据(RFLast置位);
      f. 当SDIDatSta寄存器的DatFin标志置位,确认数据操作完成;
      g. 通过对相应位写1,清除SDIDatSta的标志。

    三、MMC/SD协议

      这里我并不是要讨论MMC/SD的整个通信协议(详细的协议请看MMC/SD规范),而是遵循MMC/SD协议了解一下MMC/SD在被驱动的过程中卡所处的各种阶段和状态。根据协议,MMC/SD卡的驱动被分为:卡识别阶段和数据传输阶段。在卡识别阶段通过命令使MMC/SD处于:空闲(idle)、准备(ready)、识别(ident)、等待(stby)、不活动(ina)几种不同的状态;而在数据传输阶段通过命令使MMC/SD处于:发送(data)、传输(tran)、接收(rcv)、程序(prg)、断开连接(dis)几种不同的状态。所以可以总结MMC/SD在工作的整个过程中分为两个阶段和十种状态。下面使用图形来描述一下在两个阶段中这十种状态之间的转换关系。

    卡识别阶段,如下图:

    数据传输阶段,如下图:

    四、MMC/SD设备驱动在Linux中的结构层次

        我们翻开MMC/SD设备驱动代码在Linux源码中的位置/linux-2.6.30.4/drivers/mmc/,乍一看,分别有card、core和host三个文件夹,那哪一个文件才是我们要找的驱动代码文件啊?答案是他们都是。不是吧,听起来有些可怕,三个文件夹下有多少代码啊。呵呵,还是先放下对庞大而又神秘代码的恐惧感吧,因为在实际驱动开发中,其实只需要在host文件夹下实现你具体的MMC/SD设备驱动部分代码,现在你的心情是不是要好点了。具体的MMC/SD设备是什么意思呢?他包括RAM芯片中的SDI控制器(支持对MMC/SD卡的控制,俗称MMC/SD主机控制器)和SDI控制器与MMC/SD卡的硬件接口电路。

        那为什么刚才又说card、core和host都是MMC/SD设备的驱动呢?这就好比我们建房子,建房子首先要的是什么,地皮对吧, 有了地皮然后要到政府部门备案,备案后才能开始建,是这样的吧。在Linux中MMC/SD卡的记忆体都当作块设备。那么,我们这里的card层就是要把操作的数据以块设备的处理方式写到记忆体上或从记忆体上读取,就好比是在地皮上填沙石、挖地基等;core层则是将数据以何种格式,何种方式在MMC/SD主机控制器与MMC/SD卡的记忆体(即块设备)之间进行传递,这种格式、方式被称之为规范或协议,就好比到政府部门备案,备案就会要求你的房子应该按照怎样的行业标准进行建造;最后只剩下host层了,上面也讲到了,host层下的代码就是你要动手实现的具体MMC/SD设备驱动了,就好比现在地皮买好挖好了,建房的标准也定好了,剩下的就需要人开始动工了。

     

        那么,card、core和host这三层的关系,我们用一幅图来进行描述,图如下:

        从这幅图中的关系可以看出,整个MMC/SD模块中最重要的部分是Core核心层,他提供了一系列的接口函数,对上提供了将主机驱动注册到系统,给应用程序提供设备访问接口,对下提供了对主机控制器控制的方法及块设备请求的支持。对于主机控制器的操作就是对相关寄存器进行读写,而对于MMC/SD设备的请求处理则比较复杂。那么在主机驱动层中的一个请求处理是怎么通过核心层提交到块设备请求层的呢?

     

    在网上找到一副图来说明他们之间的关联和处理流程,如下图:


     

     

    命令、数据发送流程如下图:

     

     

    其中,黑色粗线部分为命令发送或者数据发送都要经过的流程,橙色方框部分判断所有类型的请求是否完成。

     

    下面我们就来具体实例分析一个MMC/SD卡设备驱动程序。

     

    五、实例分析MMC/SD卡设备驱动程序

    1. Mini2440开发板的MMC/SD硬件接口电路原路图如下:


      从电路原理图上可以看出,SD分别使用S3C2440的复用IO端口GPE7-10作为4根数据信号线、使用GPE6作命令信号线、使用GPE5作时钟信号线,使用复用端口GPG8的外部中断功能来作SD卡的插拔检测,使用GPH8端口来判断SD卡是否写有保护。
       
    2. MMC/SD卡驱动程序的重要数据结构,该结果位于Core核心层,主要用于核心层与主机驱动层的数据交换处理。定义在/include/linux/mmc/host.h中:

      struct mmc_host 
      {
          struct device *parent;
          struct device class_dev;
          int           index;
          const struct  mmc_host_ops *ops;
          unsigned int  f_min;
          unsigned int  f_max;
          u32           ocr_avail;

      #define MMC_VDD_165_195 0x00000080    /* VDD voltage 1.65 - 1.95 */
      #define MMC_VDD_20_21   0x00000100    /* VDD voltage 2.0 ~ 2.1 */
      #define MMC_VDD_21_22   0x00000200    /* VDD voltage 2.1 ~ 2.2 */
      #define MMC_VDD_22_23   0x00000400    /* VDD voltage 2.2 ~ 2.3 */
      #define MMC_VDD_23_24   0x00000800    /* VDD voltage 2.3 ~ 2.4 */
      #define MMC_VDD_24_25   0x00001000    /* VDD voltage 2.4 ~ 2.5 */
      #define MMC_VDD_25_26   0x00002000    /* VDD voltage 2.5 ~ 2.6 */
      #define MMC_VDD_26_27   0x00004000    /* VDD voltage 2.6 ~ 2.7 */
      #define MMC_VDD_27_28   0x00008000    /* VDD voltage 2.7 ~ 2.8 */
      #define MMC_VDD_28_29   0x00010000    /* VDD voltage 2.8 ~ 2.9 */
      #define MMC_VDD_29_30   0x00020000    /* VDD voltage 2.9 ~ 3.0 */
      #define MMC_VDD_30_31   0x00040000    /* VDD voltage 3.0 ~ 3.1 */
      #define MMC_VDD_31_32   0x00080000    /* VDD voltage 3.1 ~ 3.2 */
      #define MMC_VDD_32_33   0x00100000    /* VDD voltage 3.2 ~ 3.3 */
      #define MMC_VDD_33_34   0x00200000    /* VDD voltage 3.3 ~ 3.4 */
      #define MMC_VDD_34_35   0x00400000    /* VDD voltage 3.4 ~ 3.5 */
      #define MMC_VDD_35_36   0x00800000    /* VDD voltage 3.5 ~ 3.6 */

          unsigned long       caps;         /* Host capabilities */

      #define MMC_CAP_4_BIT_DATA    (1 << 0)/* Can the host do 4 bit transfers */
      #define MMC_CAP_MMC_HIGHSPEED (1 << 1)/* Can do MMC high-speed timing */
      #define MMC_CAP_SD_HIGHSPEED  (1 << 2)/* Can do SD high-speed timing */
      #define MMC_CAP_SDIO_IRQ      (1 << 3)/* Can signal pending SDIO IRQs */
      #define MMC_CAP_SPI           (1 << 4)/* Talks only SPI protocols */
      #define MMC_CAP_NEEDS_POLL    (1 << 5)/* Needs polling for card-detection */
      #define MMC_CAP_8_BIT_DATA    (1 << 6)/* Can the host do 8 bit transfers */

          /* host specific block data */
          unsigned int    max_seg_size;   /* see blk_queue_max_segment_size */
          unsigned short  max_hw_segs;    /* see blk_queue_max_hw_segments */
          unsigned short  max_phys_segs;  /* see blk_queue_max_phys_segments */
          unsigned short  unused;
          unsigned int    max_req_size;   /* maximum number of bytes in one req */
          unsigned int    max_blk_size;   /* maximum size of one mmc block */
          unsigned int    max_blk_count;  /* maximum number of blocks in one req */

          /* private data */
          spinlock_t      lock;  /* lock for claim and bus ops */

          struct mmc_ios  ios;   /* current io bus settings */
          u32             ocr;   /* the current OCR setting */

          /* group bitfields together to minimize padding */
          unsigned int        use_spi_crc:1;
          unsigned int        claimed:1;    /* host exclusively claimed */
          unsigned int        bus_dead:1;   /* bus has been released */
      #ifdef CONFIG_MMC_DEBUG
          unsigned int        removed:1;    /* host is being removed */
      #endif

          struct mmc_card     *card;        /* device attached to this host */

          wait_queue_head_t   wq;

          struct delayed_work    detect;

          const struct mmc_bus_ops *bus_ops;  /* current bus driver */
          unsigned int        bus_refs;       /* reference counter */

          unsigned int        sdio_irqs;
          struct task_struct  *sdio_irq_thread;
          atomic_t            sdio_irq_thread_abort;

      #ifdef CONFIG_LEDS_TRIGGERS
          struct led_trigger  *led;        /* activity led */
      #endif

          struct dentry       *debugfs_root;

          unsigned long       private[0] ____cacheline_aligned;
      };


       
    3. MMC/SD卡驱动程序的头文件中一些变量的定义,这些变量在驱动中都会用到。先不用看这些变量将用做什么,等驱动中用到时自然就明白了。代码如下:

      #define S3CMCI_DMA 0

      enum s3cmci_waitfor 
      {
          COMPLETION_NONE,
          COMPLETION_FINALIZE,
          COMPLETION_CMDSENT,
          COMPLETION_RSPFIN,
          COMPLETION_XFERFINISH,
          COMPLETION_XFERFINISH_RSPFIN,
      };

      struct s3cmci_host 
      {
          struct platform_device    *pdev;

          struct s3c24xx_mci_pdata  *pdata;
          struct mmc_host           *mmc;
          struct resource           *mem;
          struct clk                *clk;
          void __iomem              *base;
          int                       irq;
          int                       irq_cd;
          int                       dma;

          unsigned long             clk_rate;
          unsigned long             clk_div;
          unsigned long             real_rate;
          u8                        prescaler;

          unsigned                  sdiimsk;
          unsigned                  sdidata;
          int                       dodma;
          int                       dmatogo;

          struct mmc_request        *mrq;
          int                       cmd_is_stop;

          spinlock_t                complete_lock;
          enum s3cmci_waitfor       complete_what;

          int                       dma_complete;

          u32                       pio_sgptr;
          u32                       pio_bytes;
          u32                       pio_count;
          u32                       *pio_ptr;
      #define XFER_NONE             0
      #define XFER_READ             1
      #define XFER_WRITE            2
          u32                       pio_active;

          int                       bus_width;

          char                      dbgmsg_cmd[301];
          char                      dbgmsg_dat[301];
          char                      *status;

          unsigned int              ccnt, dcnt;
          struct tasklet_struct     pio_tasklet;

      #ifdef CONFIG_CPU_FREQ
          struct notifier_block     freq_transition;
      #endif
      };


       
    4. MMC/SD卡驱动程序的加载与卸载部分:
      在Linux中,MMC/SD设备是被作为平台设备添加到系统的。可以查看内核代码:/arch/arm/plat-s3c24xx/devs.c中为MMC/SD主机控制器SDI定义了平台设备和平台设备资源,然后在/arch/arm/mach-s3c2440/mach-smdk2440.c中的系统初始化的时候添加到系统中。如下:

      //平台设备资源
      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,
          }

      };


      //定义SDI平台设备
      struct platform_device s3c_device_sdi = {
          .name         = "s3c2410-sdi",
          .id         = -1,
          .num_resources     = ARRAY_SIZE(s3c_sdi_resource),
          .resource     = s3c_sdi_resource,
      };

      EXPORT_SYMBOL(s3c_device_sdi);

      //添加SDI平台设备到平台设备列表
      static struct platform_device *smdk2440_devices[] __initdata = {
          &s3c_device_usb,
          &s3c_device_sdi,
          &s3c_device_lcd,
          &s3c_device_wdt,
          &s3c_device_rtc,
          &s3c_device_dm9000,
          .
          .
          .
      };

      //平台设备添加到系统
      static void __init smdk2440_machine_init(void)
      {
          .
          .
          .
          platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
          smdk_machine_init();
      }



      所以,MMC/SD设备驱动程序的加载和卸载部分很简单,就是注册和注销平台设备,代码如下:

      //加载
      static int __init s3cmci_init(void)
      {
          platform_driver_register(&s3cmci_driver);
          return 0;
      }

      //卸载
      static void __exit s3cmci_exit(void)
      {
          platform_driver_unregister(&s3cmci_driver);
      }

      //平台设备操作结构体
      static struct platform_driver s3cmci_driver = {
          .driver.name    = "s3c2410-sdi",//名称和平台设备定义中的对应
          .driver.owner   = THIS_MODULE,
          .probe          = s3cmci_probe,//平台设备探测接口函数
          .remove         = __devexit_p(s3cmci_remove),//__devexit_p的作用以前将过
          .shutdown       = s3cmci_shutdown,
          .suspend        = s3cmci_suspend,
          .resume         = s3cmci_resume,
      };


       
    5. 平台探测函数s3cmci_probe的讲解:

      static int __devinit s3cmci_probe(struct platform_device *pdev)
      {
          //该结构体定义在头文件中,现在实例一个名为host的结构体指针为结构体中的成员赋值做准备
          struct s3cmci_host *host;
          //实例一个名为mmc的结构体指针,用于与Core核心层中的mmc_host结构体指针相关联
          struct mmc_host    *mmc;
          int ret;
          
          //初始化一个名为complete_lock的自旋锁以备后用,该自旋锁的定义在s3cmci_host结构体中
          spin_lock_init(&host->complete_lock);
          
          //初始化一个名为pio_tasklet的tasklet,用于实现中断的底半部机制,底半部服务函数为pio_tasklet,
          //将host结构体变量作为服务函数的参数。注意:这里tasklet的变量名与服务函数名称同名了(这是可以的)。
          tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);

          //分配mmc_host结构体指针的内存空间大小,该函数在host.c中实现,这里要注意一点,为什么参数
          //是s3cmci_host结构体的大小,到host.c中看,实际这里分配的是mmc_host加s3cmci_host的大小。
          mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
          if (!mmc) 
          {
              ret = -ENOMEM;
              goto probe_out;
          }

          //调用mmc_priv函数将mmc_host和s3cmci_host结构体的对象关联起来,mmc_priv定义在host.h中
          host = mmc_priv(mmc);
          
          //下面就开始初始化s3cmci_host结构体的各成员
          host->mmc     = mmc;
          host->pdev    = pdev;

          host->pdata   = pdev->dev.platform_data;
          
          //SDI主机控制器的中断屏蔽寄存器和数据寄存器,他们定义在mach-s3c2410/include/mach/regs-sdi.h中
          host->sdiimsk    = S3C2440_SDIIMSK;
          host->sdidata    = S3C2440_SDIDATA;
          
          //complete_what定义在s3cmci_host结构体中,用来记录请求处理所处的当前状态,这里初始化为
          //COMPLETION_NONE即无状态,定义在头文件的s3cmci_waitfor中,里面枚举了6种状态。
          host->complete_what = COMPLETION_NONE;
          
          //pio_active定义在s3cmci_host结构体中,用来标记请求处理数据在FIFO方式下的数据方向是读还是写
          host->pio_active     = XFER_NONE;
          
          //dodma和dma方便用于标记是否要使用DMA数据传输方式和DMA通道资源,0表示不使用DMA功能
          host->dodma        = 0;
          host->dma    = S3CMCI_DMA;
          
          //从SDI平台设备资源中获取SDI的IO端口资源,该资源在plat-s3c24xx/devs.c的s3c_sdi_resource中指定的
          host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
          if (!host->mem)
          {
              dev_err(&pdev->dev, "failed to get io memory region resouce.\n");
              ret = -ENOENT;
              goto probe_free_host;
          }
          //申请SDI的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区别)
          host->mem = request_mem_region(host->mem->start, RESSIZE(host->mem), pdev->name);
          if (!host->mem) 
          {
              dev_err(&pdev->dev, "failed to request io memory region.\n");
              ret = -ENOENT;
              goto probe_free_host;
          }

          //将SDI的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
        //注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作。
          host->base = ioremap(host->mem->start, RESSIZE(host->mem));
          if (!host->base) {
              dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
              ret = -EINVAL;
              goto probe_free_mem_region;
          }

          //同样从SDI平台设备资源中获取SDI的中断号
          host->irq = platform_get_irq(pdev, 0);
          if (host->irq == 0) 
          {
              dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
              ret = -EINVAL;
              goto probe_iounmap;
          }

          //申请SDI的中断服务,服务函数为s3cmci_irq,主要参数为host
          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;
          }

          //在SDI未准备好之前先屏蔽SDI的中断功能
          disable_irq(host->irq);

          //根据开发板原理图分别设置GPG8、GPH8端口为SD卡插入拔出的检测和有无写保护的检查,

          //注意:其实有没有写保护就是检查SD卡侧面有个移动按钮的开关,MMC卡无此功能

          host->pdata->gpio_detect = S3C2410_GPG8;

          host->pdata->gpio_wprotect = S3C2410_GPH8;

          //获取GPG8复用端口中断功能的中断号
          host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
          //GPG8是复用端口,要使用中断功能则要配置成中断功能,GPG8对应的中断功能是外部中断EINT16,这个数据手册上有讲到
          s3c2410_gpio_cfgpin(S3C2410_GPG8, S3C2410_GPG8_EINT16);

          //申请SDI的卡检测中断服务,服务函数为s3cmci_irq_cd,主要参数也为host
          if (request_irq(host->irq_cd, s3cmci_irq_cd, 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_irq;
          }

          //获取DMA通道并申请DMA中断,S3C2440中DMA的使用在后续的文章中再了解
          if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) 
          {
              dev_err(&pdev->dev, "unable to get DMA channel.\n");
              ret = -EBUSY;
              goto probe_free_irq_cd;
          }

          //从平台时钟队列中获取SDI的时钟源,在arch/arm/plat-s3c24xx/s3c2410-clock.c中有定义
          host->clk = clk_get(&pdev->dev, "sdi");
          if (IS_ERR(host->clk)) 
          {
              dev_err(&pdev->dev, "failed to find clock source.\n");
              ret = PTR_ERR(host->clk);
              host->clk = NULL;
              goto probe_free_host;
          }

          //启动获取的时钟源
          ret = clk_enable(host->clk);
          if (ret) 
          {
              dev_err(&pdev->dev, "failed to enable clock source.\n");
              goto clk_free;
          }

          //通过SDI的时钟源获取CPU的PCLK频率,这里为什么要获得CPU的PCLK频率呢,
          //通过数据手册SDI控制器的方框图得知,SDI的时钟频率(SDCLK)=PCLK/(Prescaler+1)
          host->clk_rate = clk_get_rate(host->clk);
          host->clk_div    = 1;//设置预分频值,即:Prescaler的值
          
          //下面对mmc_host进行初始化
          mmc->ops       = &s3cmci_ops;    //SDI主机控制器操作结构体
          mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;   //设置工作电压范围
          mmc->caps      = MMC_CAP_4_BIT_DATA;              //设置总线宽度为4位
          mmc->f_min     = host->clk_rate / (host->clk_div * 256); //设置最小工作频率
          mmc->f_max     = host->clk_rate / host->clk_div;  //设置最大工作频率
          mmc->max_blk_count  = 4095;
          mmc->max_blk_size   = 4095;
          mmc->max_req_size   = 4095 * 512;
          mmc->max_seg_size   = mmc->max_req_size;
          mmc->max_phys_segs  = 128;
          mmc->max_hw_segs    = 128;

          //Linux的通知链机制,实现到后面再讲
          ret = s3cmci_cpufreq_register(host);
          if (ret) 
          {
              dev_err(&pdev->dev, "failed to register cpufreq\n");
              goto free_dmabuf;
          }

          //将SDI host设备注册到系统中
          ret = mmc_add_host(mmc);
          if (ret) 
          {
              dev_err(&pdev->dev, "failed to add mmc host.\n");
              goto free_cpufreq;
          }

          //将SDI host设备的数据赋值给系统平台设备
          platform_set_drvdata(pdev, mmc);

          return 0;

      //以下是错误处理
       free_cpufreq:
          s3cmci_cpufreq_deregister(host);

       free_dmabuf:
          clk_disable(host->clk);

       clk_free:
          clk_put(host->clk);

       probe_free_irq_cd:
          if (host->irq_cd >= 0)
              free_irq(host->irq_cd, host);

       probe_free_irq:
          free_irq(host->irq, host);

       probe_iounmap:
          iounmap(host->base);

       probe_free_mem_region:
          release_mem_region(host->mem->start, RESSIZE(host->mem));

       probe_free_host:
          mmc_free_host(mmc);
       probe_out:
          return ret;
      }


      Linux的通知链机制

      //Linux的通知链机制,需要内核配置时的支持。
      //这个通知链机制在MMC/SD卡驱动中应用的目的是,当CPU频率发生改变时,MMC/SD时钟频率也要改变。
      //具体通知链机制的原理请看下一篇转载的文章。
      #ifdef CONFIG_CPU_FREQ

      static int s3cmci_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data)
      {
          struct s3cmci_host *host;
          struct mmc_host *mmc;
          unsigned long newclk;
          unsigned long flags;

          host = container_of(nb, struct s3cmci_host, freq_transition);
          newclk = clk_get_rate(host->clk);
          mmc = host->mmc;

          if ((val == CPUFREQ_PRECHANGE && newclk > host->clk_rate) ||
           (val == CPUFREQ_POSTCHANGE && newclk < host->clk_rate)) {
              spin_lock_irqsave(&mmc->lock, flags);

              host->clk_rate = newclk;

              if (mmc->ios.power_mode != MMC_POWER_OFF &&
               mmc->ios.clock != 0)
                  s3cmci_set_clk(host, &mmc->ios);

              spin_unlock_irqrestore(&mmc->lock, flags);
          }

          return 0;
      }

      static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
      {
          host->freq_transition.notifier_call = s3cmci_cpufreq_transition;

          return cpufreq_register_notifier(&host->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
      }

      static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
      {
          cpufreq_unregister_notifier(&host->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
      }

      #else//如果内核配置时没有选择此功能支持,则其实现为空即可
      static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
      {
          return 0;
      }



      从探测函数中可以看到,我们接下来要实现的功能就很清晰了。他们分别是:
      a. s3cmci_ops SDI主机控制器操作接口函数功能;
      b. s3cmci_irq_cd SDI的卡检测中断服务功能;
      c. s3cmci_irq SDI的中断服务功能;
    展开全文
  • 提出一种在FPGA NiosⅡ软核处理器下SD卡驱动设计的方法。采用Altera公司的FPGA可编程逻辑器件,构建了NiosⅡ 软核处理器平台,并在此之上实现了SD卡的驱动设计。实验结果表明:设计提高了FPGA系统的设计灵活度,并...
  • msm8916 5.1: kernel/drivers/mmc/host/sdhci-msm.c sdhci_msm_populate_pdata函数解析dts sdhci_msm_probe-------probe ...devm_request_threaded_irq------从参数中得出中断处理函数 mmc_g...

    msm8916 5.1:

    kernel/drivers/mmc/host/sdhci-msm.c

    sdhci_msm_populate_pdata函数解析dts

     

    sdhci_msm_probe-------probe

    mmc_gpio_request_cd----------注册中断

    devm_request_threaded_irq------从参数中得出中断处理函数 mmc_gpio_cd_irqt

    mmc_gpio_cd_irqt----中断处理函数里调用mmc_detect_change-----开启delayed_work------mmc_schedule_delayed_work根据参数找到delayed_work的注册和处理函数---------mmc_rescan(core/core.c)----INIT_DELAYED_WORK(&host->detect, mmc_rescan);(core/host.c)

     

     

     

     

    sdm450 7.0:

    kernel/drivers/mmc/host/sdhci-msm.c

    sdhci_msm_populate_pdata函数解析dts

     

    sdhci_msm_probe-------probe

    mmc_gpio_request_cd----------注册中断

     

    mmc_gpio_cd_irqt--(slot-gpio.c)--中断处理函数里调用mmc_detect_change-----开启work

    ----mmc_rescan(core/core.c)

    ----INIT_DELAYED_WORK(&host->detect, mmc_rescan);(core/host.c)

    展开全文
  • sd卡驱动分析

    2014-10-28 15:22:23
    linux下,sd卡工作原理,及驱动工作机制详细分析
  • S5PV210裸机SD卡驱动

    2017-11-16 21:33:22
    S5PV210裸机开发之SD卡驱动,驱动包含一个C文件、一个头文件和测试main文件,经过测试,可以读、写、擦除SD卡的一个或多个数据块。该驱动只分析4位sd模式、sd2.0及sd1.0版本的sd卡驱动实现,sd2.0以上版本sd卡、MMC...
  • linux SD卡驱动分析

    2016-04-25 11:35:42
    linux SD卡驱动分析
  • 引言  SD卡(Secure Digital Memory...因此SD卡被广泛使用于嵌入式便携移动装置上作为嵌入式系统的数据存储设备,如:数码相机、PDA和多媒体播放器等。SD卡用于ReWorks嵌入式系统,必须要开发相应的驱动程序。  1 S
  • TB SD卡驱动移植实验

    2020-08-20 05:06:51
    TB SD卡驱动移植实验
  • 近年来,基于FPGA的软核处理器以其高度的设计灵活性和低成本在嵌入式市场中得到重视并不断发展。其中具有代表性的软核...这里将结合NiosⅡ处理器的总线架构,分析SD卡的接口协议和驱动程序设计方法,并给出SD卡设备在N
  • Linux SD卡驱动开发

    2020-05-24 16:17:07
    Linux SD卡驱动开发(一) —— SD 相关基础概念 Linux SD卡驱动开发(二) —— SD 卡驱动分析HOST...Linux SD卡驱动开发(六) —— SD卡启动过程总体分析 一.SD/MMC卡基础概念 1.1.什么是MMC卡 MMC:MMC就是MultiM...

    Linux SD卡驱动开发(一) —— SD 相关基础概念

    Linux SD卡驱动开发(二) —— SD 卡驱动分析HOST篇

    Linux SD卡驱动开发(三) —— SD 卡驱动分析CORE篇

    Linux SD卡驱动开发(四) —— SD 控制器之真正的硬件操作

    Linux SD卡驱动开发(五) —— SD 卡驱动分析Core补充篇

    Linux SD卡驱动开发(六) —— SD卡启动过程总体分析

    一.SD/MMC卡基础概念

    1.1.什么是MMC卡

          MMC:MMC就是MultiMediaCard的缩写,即多媒体卡。它是一种非易失性存储器件,体积小巧(24mm*32mm*1.4mm),容量大,耗电量低,传输速度快,广泛应用于消费类电子产品中。

    1.2.什么是SD卡

         SD:SD卡为Secure Digital Memory Card, 即安全数码卡。它在MMC的基础上发展而来,增加了两个主要特色:SD卡强调数据的安全安全,可以设定所储存的使用权限,防止数据被他人复制;另外一个特色就是传输速度比2.11版的MMC卡快。在数据传输和物理规范上,SD卡(24mm*32mm*2.1mm,比 MMC卡更厚一点),向前兼容了MMC卡.所有支持SD卡的设备也支持MMC卡。SD卡和2.11版的MMC卡完全兼容。

    1.3.什么是SDIO

       SDIO:SDIO是在SD标准上定义了一种外设接口,它和SD卡规范间的一个重要区别是增加了低速标准。在SDIO卡只需要SPI和1位SD传输模式。低速卡的目标应用是以最小的硬件开销支持低速IO能力。

    1.4.什么是MCI 

       MCI:MCI是Multimedia Card Interface的简称,即多媒体卡接口。上述的MMC,SD,SDI卡定义的接口都属于MCI接口。MCI这个术语在驱动程序中经常使用,很多文件,函数名字都包括”mci”.

    1.5.MMC/SD/SDIO卡的区别

         SDIO 是目前我们比较关心的技术,SDIO 故名思义,就是 SD 的 I/O 接口(interface )的意思,不过这样解释可能还有点抽像。更具体的说明,SD 本来是记忆卡的标准,但是现在也可以把 SD 拿来插上一些外围接口使用,这样的技术便是 SDIO 。

         所以 SDIO 本身是一种相当单纯的技术,透过 SD 的 I/O 接脚来连接外部外围,并且透过 SD 上的I/O 数据接位与这些外围传输数据,而且 SD 协会会员也推出很完整的 SDIO stack 驱动程序,使得SDIO 外围(我们称为 SDIO 卡)的开发与应用变得相当热门。

        现在已经有非常多的手机或是手持装置都支持 SDIO 的功能(SD 标准原本就是针对 mobile device而制定),而且许多 SDIO 外围也都被开发出来,让手机外接外围更加容易,并且开发上更有弹性(不需要内建外围)。目前常见的 SDIO 外围(SDIO 卡)有:

    ·Wi-Fi card (无线网络卡)
    ·CMOS sensor card (照相模块)
    ·GPS card
    ·GSM/GPRS modem card
    ·Bluetooth card
    ·Radio/TV card (很好玩)

    SDIO 的应用将是未来嵌入式系统最重要的接口技术之一,并且也会取代目前 GPIO 式的 SPI 接口。

    二、开发板SD资源

            以Exynos4412开发板为例,其SD卡硬件原理图如下:

          图中可以看到,SD卡设备的连接方式就是SDIO总线的驱动方式,这里使用EINT7作为NCD的控制器,当SD卡设备插入/取出时均会中断响应。

    三、 SD协议概要

    1、 总线接口  

    按照SD卡的协议的描述可分为2种总线的接口 

    SD BUS   

    物理层定义:  

    D0-D3 数据传送  

    CMD 进行CMD 和Respons   

    CLK 大家最熟悉的HOST时钟信号线了 

    VDD VSS 电源和地   

    SPI BUS 

    一般用SPI协议的接口来做 

    物理层定义:  

    CLK HOST时钟信号线了  

    DATAIN HOST-àSD Card数据信号线 

    DATAOUT SD Card àHOST数据信号线

    2、请求处理流程

         根据协议,MMC/SD卡的驱动被分为:卡识别阶段和数据传输阶段。

        在卡识别阶段通过命令使MMC/SD处于:空闲(idle)、准备(ready)、识别(ident)、等待(stby)、不活动(ina)几种不同的状态;

        而在数据传输阶段通过命令使MMC/SD处于:发送(data)、传输(tran)、接收(rcv)、程序(prg)、断开连接(dis)几种不同的状态。

    所以可以总结MMC/SD在工作的整个过程中分为两个阶段和十种状态。下面使用图形来描述一下在两个阶段中这十种状态之间的转换关系。

    a -- 卡识别阶段

    b -- 数据传输阶段

    四、 MMC/SD设备驱动在Linux中的结构层次  

     在Linux中MMC/SD卡的记忆体都当作块设备。MMC/SD设备驱动代码在linux-2.6.38.2\drivers\mmc  分别有card、core和host三个文件夹,    

    card层    要把操作的数据以块设备的处理方式写到记忆体上或从记忆体上读取; 

    core层    则是将数据以何种格式,何种方式在 MMC/SD主机控制器与MMC/SD卡的记 忆体(即块设备)之间进行传递,这种格式、方式被称之为规范或协议, 

    host层   下的代码就是你要动手实现的具体MMC/SD设备驱动了,包括RAM芯片中的 SDI控制器(支持对MMC/SD卡的控制,俗称MMC/SD主机控制器)和SDI控制器与MMC/SD卡的硬件接口电路。       

    那么,card、core和host这三层的关系,我们用一幅图来进行描述,图如下:

          从这幅图中的关系可以看出,整个MMC/SD模块中最重要的部分是Core核心层,他提供了一系列的接口函数,对上提供了将主机驱动注册到系统,给应用程序提供设备访问接口,对下提供了对主机控制器控制的方法及块设备请求的支持。对于主机控制器的操作就是对相关寄存器进行读写,而对于MMC/SD设备的请求处理则比较复杂。

    展开全文
  • SD卡驱动程序、资料

    2018-08-08 10:49:54
    51单片机、STM32单片机,SD卡驱动程序、原理图及相关资料,亲测通过
  • 绍Linux2.6.18 SD卡驱动的修正步骤。
  • 嵌入式linux 2.6内核的SD卡驱动程序,本人已测试成功!
  • SD卡驱动程序(51单片机)初始化SD卡到SPI模式 unsigned char SD_Init() { unsigned char retry,temp; unsigned char i; unsigned char CMD[] = {0x40,0x00,0x00,0x00,0x00,0x95}; SD_Port_Init(); //初始化...
  •  Linux在内核源码的drivers/mmc/core文件夹下为我们的提供了一系列SD卡的接口服务函数。可以查看Makefile如下 可见,core文件夹下有针对总线的服务bus.c,针对主控制器的服务host.c,针对SD卡的服务sd.c, sd_ops...

           废话不多说,直接切进主题:

           Linux在内核源码的drivers/mmc/core文件夹下为我们的提供了一系列SD卡的接口服务函数。可以查看Makefile如下


    可见,core文件夹下有针对总线的服务bus.c,针对主控制器的服务host.c,针对SD卡的服务sd.c, sd_ops.c等等。

    其中,最为核心的一个函数便是之前提到的位于core.c的mmc_rescan,概括来讲,主要完成两项任务,即

    扫描SD总线,插入SD卡
    扫描SD总线,拔出SD卡


    一、 插入SD卡

            前面HOST篇最后的中断篇中讲到,插入SD卡,主控制器产生中断,进入中断处理函数s3cmci_irq_cd,其中调用的函数 mmc_detect_change,它将最终调用queue_delayed_work执行工作队列里的mmc_rescan函数

    下面来看看 mmc_rescan

    void mmc_rescan(struct work_struct *work)
    {
    	struct mmc_host *host =
    		container_of(work, struct mmc_host, detect.work);
    	int i;
    
    	if (host->rescan_disable)
    		return;
    
    	/* If there is a non-removable card registered, only scan once */
    	if ((host->caps & MMC_CAP_NONREMOVABLE) && host->rescan_entered)
    		return;
    	host->rescan_entered = 1;
    
    	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);
    
    	host->detect_change = 0;
    
    	/*
    	 * 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->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd &&
    			host->ops->get_cd(host) == 0) {
    		mmc_claim_host(host);
    		mmc_power_off(host);
    		mmc_release_host(host);
    		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);
    }
    

          插入SD卡,mmc_rescan扫描SD总线上是否存在SD卡,具体的实现方法就是通过向SD卡上电,看是否能成功,以普通SD卡为例,为普通SD卡上电的函数mmc_send_app_op_cond(host, 0, &ocr);

    如果上电成功,则返回0,即执行if()里的mmc_attach_sd()进行总线与SD卡的绑定

    如果上电失败,则返回非0值,跳过if(),尝试其他上电的方法。

         那么,上电方法究竟有何不同呢?具体看看mmc_send_app_op_cond()的实现过程

    int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
    {
    	struct mmc_command cmd;
    	cmd.opcode = SD_APP_OP_COND;    /* #define SD_APP_OP_COND   41   */
    	mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
    
    	... ...
    
    }
    int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, struct mmc_command *cmd, int retries)
    {
    
      mmc_app_cmd(host, card);   /* #define MMC_APP_CMD   55   */
      mrq.cmd = cmd;
      cmd->data = NULL;
    
      mmc_wait_for_req(host, &mrq);
    
      ... ...
    
    }

    这里的指令SD_APP_OP_COND只有SD2.0的协议支持,也就是说,只有普通SD卡支持,所以也只有普通SD卡能够成功上电。 

                 


    如果上电成功,就开始进行总线与SD卡的绑定,通过mmc_attach_sd(),绑定过程可分为四步,

    注册SD总线上的操作函数 - struct mmc_bus_ops mmc_sd_ops

    设置主控制器的时钟和总线方式 - 通过回调函数host->ops->set_ios();

    启动SD卡 - 根据协议,完成SD卡启动的各个步骤

    注册SD卡设备驱动


    二、注册总线上的操作函数

    int mmc_attach_sd(struct mmc_host *host, u32 ocr)
    {
    	mmc_sd_attach_bus_ops(host);
    
     	... ...
    
    }
    

    static void mmc_sd_attach_bus_ops(struct mmc_host *host)
    {
    	const struct mmc_bus_ops *bus_ops;
    
    	bus_ops = &mmc_sd_ops;
    	mmc_attach_bus(host, bus_ops);
    }
    void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
    {
    	host->bus_ops = ops;
    	host->bus_refs = 1;
    	host->bus_dead = 0;
    }
    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,  // 重新启动SD卡的操作函数
    };

         这里的mmc_sd_detect和mmc_sd_remove就是拔出SD卡所需要用到的函数,下文将详细讨论。这里需要注意的是,插入SD卡的时候,并不执行mmc_sd_detect和mmc_sd_remove这两个函数,但是会注册它们,也就是说,这两个函数的功能已经实现,将来可以使用。


    三、设置时钟和总线

    int mmc_attach_sd(struct mmc_host *host, u32 ocr)
    {
    	host->ocr = mmc_select_voltage(host, ocr);
    
    	... ...
    
    }
    
    u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
    {
    	mmc_set_ios(host);
    
    	... ...
    }
    
    static inline void mmc_set_ios(struct mmc_host *host)
    {
    	struct mmc_ios *ios = &host->ios;
    
    	host->ops->set_ios(host, ios);  // 设置主控制器时钟和总线的回调函数,具体实现由主控制器驱动完成
    }

        从这里可以体会到回调函数的精髓:协议层里利用回调函数为所有满足该协议的设备提供统一的接口,而具体实现由底层不同的设备驱动各自完成。注意到,之所以要定义一些放之四海而皆准的公用的类,比如struct mmc_host,就是需要通过struct mmc_host *host指针作为形参传到协议层所提供的接口函数中,从而得以调用。


    四、启动SD卡

    int mmc_attach_sd(struct mmc_host *host, u32 ocr)
    {
    
    	mmc_sd_init_card(host, host->ocr, NULL);
    
    	... ...
    
    }

     mmc_sd_init_card主要完成以下任务,

    SD卡的启动过程
    得到寄存器CID, CSD, SCR, RCA的数据
    其他操作比如切换到高速模式,初始化card

    static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard)
    {
    	
    	/* SD卡的启动过程 */
    	mmc_go_idle(host);
    	mmc_send_if_cond(host, ocr);
    	mmc_send_app_op_cond(host, ocr, NULL);
    	mmc_all_send_cid(host, cid);
    	mmc_send_relative_addr(host, &card->rca);
    	
    	/* 得到寄存器CID, CSD, SCR的数据 */
    	mmc_send_csd(card, card->raw_csd);
    	mmc_decode_csd(card);
    	mmc_decode_cid(card);
    	mmc_app_send_scr(card, card->raw_scr);
    	mmc_decode_scr(card);
    
    	/* 其它操作 */
    	mmc_alloc_card(host, &sd_type);
    	mmc_select_card(card); 
    	mmc_read_switch(card);
    	mmc_switch_hs(card);
    	... ...
    
    }


    1) SD卡的启动过程

        根据SD2.0协议,SD卡的状态可分为两种模式:卡识别模式(card-identification mode)和数据传输模式(data-transfer mode)。这里,我们关注启动SD卡的卡识别模式。


    综合代码:

      mmc_go_idle(host);                     CMD0
      Idle State
      mmc_send_if_cond(host, ocr);     CMD8
      mmc_send_app_op_cond(host, ocr, NULL);       ACMD41
      Ready State
      mmc_all_send_cid(host, cid);       CMD2
      Identification State
      mmc_send_relative_addr(host, &card->rca);     CMD3
      Stand-by State

    2) 寄存器CID, CSD, SCR, RCA

    -> 发送指令并得到寄存器的值

       当主控制器向SD卡发送cmd指令,比如mmc_send_cid(card, card->raw_cid),请求得到SD卡CID寄存器的值,当主控制器发送cmd完成后,芯片产生一个内部中断,处理结束cmd的中断函数,之后得到来自SD卡的response,即CID寄存器的值,存放于host->cmd->resp[i]中。关于内部中断处理,参看上文的中断一节里的 mmc_wait_for_cmd()

       mmc_send_cid(card, card->raw_cid);这个函数发送了接收CSD寄存器的请求,并且得到了来自SD卡的CSD寄存器的值。

    int mmc_send_cid(struct mmc_card *card, u32 *cid)
    {
       return mmc_send_cxd_native(card->host, card->rca << 16, cid, MMC_SEND_CID);
    
    }
    
    static int mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
    {
     cmd.opcode = opcode;
     cmd.arg = arg;
     cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
    
     mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
    
     memcpy(cxd, cmd.resp, sizeof(u32) * 4);  // 得到response赋给cxd,即card->raw_cid
    
     ... ...
    }

    -> 解析寄存器的值

        为什么要解析?先来看看寄存器CID在SD卡协议里的定义,它是一个128位的寄存器,存放了关于这块SD卡的基本信息,就像自己的身份证。通过mmc_send_cid()将这个寄存器的数值赋给了card->raw_cid (定义 u32 raw_cid[4];) ,为了方便得到具体某一个信息,协议层为我们解析了寄存器里的域,并赋给card->cid,比如厂商名称,就可以通过card->cid.manfid直接读取到。

    static int mmc_decode_cid(struct mmc_card *card)
    {
      u32 *resp = card->raw_cid;
    
      card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
      card->cid.oemid  = UNSTUFF_BITS(resp, 104, 16);
      card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
      card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
      card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
      card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
      card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
      card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8);
      card->cid.serial = UNSTUFF_BITS(resp, 16, 32);
      card->cid.month  = UNSTUFF_BITS(resp, 12, 4);
      card->cid.year  = UNSTUFF_BITS(resp, 8, 4) + 1997;
      return 0;
    }

    五、 注册SD卡设备驱动

     int mmc_attach_sd(struct mmc_host *host, u32 ocr)
    {
    
      /* mmc_alloc_card(host, &sd_type); 在mmc_sd_init_card()已完成 */
    
      mmc_add_card(host->card);
    
      ... ...
    
    }

    上文已经提到,设备驱动程序都会通过alloc_xxx()和add_xxx()两步来注册驱动,其实质是调用/drivers/base/core.c里的device_initialize()和device_add(),device_add()完成建立kobject,sys文件,发送uevent,等工作。


    六、拔出SD卡

     void mmc_rescan(struct work_struct *work)
    {
     struct mmc_host *host = container_of(work, struct mmc_host, detect.work);
     mmc_bus_get(host);
    
     /* 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);
    
     mmc_bus_put(host);
    
     ... ...
    
    }

    这里的mmc_bus_get/put(),为SD总线加上一个自旋锁,规定同时只能有一个线程在SD总线上操作。


    1、 bus_ops->detect()

           mmc_rescan()扫描SD总线,如果发现host->ops上赋了值,即之前已有SD卡注册过,就执行bus_ops->detect()操作去探测SD总线上是否还存在SD卡,如果不存在了,就执行bus_ops->remove()拔出SD卡。之前已经提到,这个bus_ops->detect()已在mmc_attach_sd()注册完成了。

    static void mmc_sd_detect(struct mmc_host *host)
    {
     mmc_claim_host(host);
    
     /*
      * Just check if our card has been removed.
      */
     err = mmc_send_status(host->card, NULL);
    
     mmc_release_host(host);
    
     if (err) {
      mmc_sd_remove(host);
    
      mmc_claim_host(host);
      mmc_detach_bus(host);
      mmc_release_host(host);
     }
    }

    这里的mmc_claim_host(host)通过set_current_state(TASK_RUNNING);将当前进程设置为正在运行进程。

    mmc_send_status()发送得到SD卡状态的请求,如果未能得到状态数据,则执行mmc_sd_remove(host)拔出SD卡。

    int mmc_send_status(struct mmc_card *card, u32 *status)
    {
     struct mmc_command cmd;
    
     cmd.opcode = MMC_SEND_STATUS;    /* #define MMC_SEND_STATUS   13 */
     cmd.arg = card->rca << 16;
     cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
    
     err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
    
     if (err)               
      return err;           // 接收来自SD卡的response失败,即没有发现SD卡
     if (status)
      *status = cmd.resp[0];
    
     return 0;
    
    }


    2、bus_ops->remove()

          拔出SD卡,其实就是注册SD卡驱动的反操作,实质就是执行device_del()和device_put()

    static void mmc_sd_remove(struct mmc_host *host)
    {
     mmc_remove_card(host->card);
     host->card = NULL;
    }
    void mmc_remove_card(struct mmc_card *card)
    {
     if (mmc_card_present(card))
      device_del(&card->dev);
    
     put_device(&card->dev);
    }




    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,981
精华内容 47,192
关键字:

sd卡驱动