SD card driver to resolve

First, the development environment

  • Host: VMWare - Fedora 9
  • Development Board: Mini2440--64MB Nand, Kernel: 2.6.30.4
  • Compiler: arm-linux-gcc-4.3.2

Two, MMC / SD SDI and introduced a host controller

  First, let's clarify a few concepts:

  1. MMC: (Multi Media Card) by Siemens and the devaluation of the CF SanDisk launched in 1997, multimedia memory card standard.
  2. SD: (Secure Digital Memory Card) from Japan Panasonic, Toshiba and SanDisk Corporation in the United States in August 1999 jointly developed a new generation of memory card standard, is fully compatible with MMC standards.
  3. SDIO: (Secure Digital Input and Output Card) Secure Digital Input Output cards. SDIO is defined on an SD standard peripheral interface, the peripheral device connected via the SD of the I / O pins, the I and by the SD / O access data bits for data transmission with the peripherals. It is the more popular technique, as shown in some of the equipment: GPS, camera, Wi-Fi, FM radio, bar code reader, Bluetooth and so on.
  4. Work mode: mode of operation is for the host controller for the. In other words, S3C2440 the SDI controller can work in line with the MMC standard, or can be operated in compliance with the SD standard, or can be operated in compliance with the SDIO standard. Therefore, it is abbreviated as: MMC mode, SD mode and SDIO mode.
  5. Transmission mode: a transmission mode for the host controller is for, and transmission means between the controller and the pattern data card, or a bus type. The SDI S3C2440 controller supports SPI, 1 and 4 positions of the three transmission modes (bus type). So what is SPI it? Please refer to here: the SPI protocol Introduction ; as for a four and then what does it mean? They refers to the transmission of the data bus width, the specific data sheet.

  Use the table below lists the transmission modes supported by the lower case electrical characteristics MMC, SD, SDIO and performance of different operating modes:


  Well, we now how to make the host controller to work on work mode and transmission mode we require it? It is simply the respective registers of the host controller to the corresponding configuration. The following briefly explain the purpose and structure of the host controller SDI each register.

SDI host controller in the structure of FIG S3C2440 follows:

  As shown above, SDI host controller using a serial clock line and the data line 5 and the sampling synchronization shift information. Transmission frequency is controlled by setting the corresponding bit is set SDIPRE registers may be modified to adjust the frequency of the baud rate data register.

Introduce major register, register for a specific set of bits on the data sheet:

  1. SDICON: control registers, configuration completion SD card infrastructure, including the size of the terminal, interrupt enable, mode selection, clock enable like.
  2. SDIPRE: Baud Rate Prescaler register, the configuration SDCLK.
  3. SDICmdArg: command parameter register, instruction parameters stored here.
  4. SDICCON: control instruction register, a configuration SPI SDI or instruction, feedback instruction length, whether to wait for feedback, whether the operation command, the indexing instruction.
  5. SDICmdSta: command status register, a timeout command is transmitted, the end, CRC is correct and the like.
  6. SDIRSP0-3: SD reflects the state.
  7. SDIDTimer: Set timeout.
  8. SDIBSize: block size register.
  9. SDIDatCon: The data control registers, configuration is several line transmission, data transmission direction, the data transfer method.
  10. SDIDatSta: data status register, whether sending the data, CRC efficacy, timeout.
  11. SDIFSTA: FIFO status registers, DMA transfer is determined whether the FIFO.
  12. SDIIntMsk: interrupt mask register.
  13. SDIDAT: SDI data register.

SDI host controller setup steps in the SD / MMC operating modes :( Note: Because SD MMC mode compatible mode, so we need to know to SD mode, and SDIO mode of operation is for the SDIO device, so here We will not discuss)

  1. SDICON register set to configure the appropriate clock and interrupt enable;
  2. SDIPRE register set appropriate values;
  3. Wait 74 SDCLK ​​clock to initialize the card;
  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的中断服务功能;

Guess you like

Origin blog.csdn.net/u010783226/article/details/92082396