历史上的今天

今天是:2024年10月25日(星期五)

正在发生

2021年10月25日 | S3C2440上MMC/SD卡驱动分析(二)

发布者:ularof不加糖 来源: eefocus关键字:S3C2440  MMC  SD卡  驱动分析 手机看文章 扫描二维码
随时随地手机看文章

下面的文章主要是转载的,先记录下自己的经验。


MMC/SD驱动有两种模式:FIFO和DMA。在代码中两种方式都予以了实现,在make menuconfig时候,可以选择是使用fifo方式还是DMA方式。其中FIFO方式就是 向FIFO寄存器写入和读取。FIFO模式刚开始不太明白,FIFO就是起到缓存的作用,可以提高响应速度。具体在代码的操作中,写入时就是将要发送的数据用while循环依次写入FIFO寄存器的地址(只是一个寄存器),硬件上的实现使得我们虽然写入时都是写入了FIFO寄存器(它是FIFO的起始),但是多个数据在FIFO中依次排列的,这里应该用到了移位器和锁存器。 读取时也是相同的,只读FIFO寄存器,就可以将FIFO中的寄存器都读出来。


另外,FIFO是配合中断来使用的,中断可以进行配置,当FIFO empty、half full、full的时候都可以产生中断。在有些硬件上面,可以直接设置FIFO的trigger level,就是指收到多少字节之后产生中断。


此外,MMC对应的FIFO是64bytes的。接收和发送各有一个FIFO,可以分开配置。另外,串口也有FIFO。


最后,代码中实现了的DMA方式不能正常工作,目前我还不知道怎么修改。DMA 发送发送data时候,会打印错误:buffer load timeout


一、开发环境


主  机:VMWare--Fedora 9

开发板:Mini2440--64MB Nand, Kernel:2.6.30.4

编译器:arm-linux-gcc-4.3.2

上接:S3C2440上MMC/SD卡驱动实例开发讲解(一)


6. s3cmci_ops SDI主机控制器操作接口函数功能分析:


static struct mmc_host_ops s3cmci_ops = 

{

    .request = s3cmci_request,//实现host的请求处理(即:命令和数据的发送和接收)

    .set_ios = s3cmci_set_ios,//通过核心层传递过来的ios,配置host寄存器(使能时钟、总线带宽等)

    .get_ro  = s3cmci_get_ro,//通过读取GPIO端口来判断卡是否写有保护

    .get_cd  = s3cmci_card_present,//通过读取GPIO端口来判断卡是否存在

};


mmc_host_ops结构体定义了对host主机进行操作的各种方法,其定义在Core核心层的host.h中,也就是Core核心层对Host主机层提供的接口函数。这里各种方法的函数原型如下:

void  (*request)(struct mmc_host *host, struct mmc_request *req);

void  (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);

int   (*get_ro)(struct mmc_host *host);

int   (*get_cd)(struct mmc_host *host);



从各函数原型上看,他们都将mmc_host结构体作为参数,所以我在刚开始的时候就说过mmc_host结构体是MMC/SD卡驱动中比较重要的数据结构。 可以这样说,他是Core层与Host层进行数据交换的载体。那么,这些接口函数何时会被调用呢?答案可以在Core层的core.c和sd.c中找到,我们可以看到如下部分代码:

static void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)

{

    ......

    host->ops->request(host, mrq);//导致s3cmci_request被调用

}


static inline void mmc_set_ios(struct mmc_host *host)

{

    ......

    host->ops->set_ios(host, ios);//导致s3cmci_set_ios被调用

}


void mmc_rescan(struct work_struct *work)

{

    ......//导致s3cmci_card_present被调用

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

            goto out;

    ......

}


static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,

    struct mmc_card *oldcard)

{

    ......

    /* Check if read-only switch is active.*/

    if (!oldcard) 

    {   //导致s3cmci_get_ro被调用

        if (!host->ops->get_ro || host->ops->get_ro(host) < 0) 

        {

            printk(KERN_WARNING "%s: host does not "

                "support reading read-only "

                "switch. assuming write-enable.n",

                mmc_hostname(host));

        } 

        else 

        {

            if (host->ops->get_ro(host) > 0)

                mmc_card_set_readonly(card);

        }

    }

    ......

}



好了,我们开始分析每个接口函数的具体实现吧,从简单的开始吧。 判断卡是否存在,如下代码:


static int s3cmci_card_present(struct mmc_host *mmc)

{

    //从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的

    struct s3cmci_host *host = mmc_priv(mmc);

    struct s3c24xx_mci_pdata *pdata = host->pdata;

    int ret;


    //判断有无设置卡检测引脚端口,引脚在s3cmci_probe函数中已设置

    if (pdata->gpio_detect == 0)

        return -ENOSYS;


    //从设置的卡检测引脚中读出当前的电平值,来判断卡是插入存在的还是被拔出不存在的

    ret = s3c2410_gpio_getpin(pdata->gpio_detect) ? 0 : 1;

    return ret ^ pdata->detect_invert;

}


获取卡是否写有保护,其实实现跟卡检查类似,代码如下:


static int s3cmci_get_ro(struct mmc_host *mmc)

{

    //从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的

    struct s3cmci_host *host = mmc_priv(mmc);

    struct s3c24xx_mci_pdata *pdata = host->pdata;

    int ret;


    //判断有无设置卡写保护引脚端口,引脚在s3cmci_probe函数中已设置

    if (pdata->gpio_wprotect == 0)

        return 0;


    //从设置的卡写保护引脚中读出当前的电平值,来判断卡是否写有保护

    ret = s3c2410_gpio_getpin(pdata->gpio_wprotect);


    if (pdata->wprotect_invert)

        ret = !ret;


    return ret;

}


配置host寄存器的时钟和总线宽度,代码如下:

static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

{

    //从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的

    struct s3cmci_host *host = mmc_priv(mmc);

    u32 mci_con;


    //读取SDI控制寄存器的值

    mci_con = readl(host->base + S3C2410_SDICON);


    //ios结构体参数从Core层传递过来,根据不同的电源状态来配置SDI各寄存器

    switch (ios->power_mode) 

    {

        case MMC_POWER_ON:

        case MMC_POWER_UP:

            //根据开发板引脚连接情况配置SDI控制器的各信号线,包括:时钟线、命令线和四条数据线

            s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);

            s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);

            s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);

            s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);

            s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);

            s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);

    

            if (host->pdata->set_power)

                host->pdata->set_power(ios->power_mode, ios->vdd);

    

            break;

    

        case MMC_POWER_OFF:

        default:

            //如果电源状态为关闭或者默认情况下,关闭SDI的时钟信号

            s3c2410_gpio_setpin(S3C2410_GPE5, 0);

            s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);

    

            //根据数据手册的SDICON寄存器位的介绍,此处是将整个sdmmc时钟复位

            mci_con |= S3C2440_SDICON_SDRESET;

    

            if (host->pdata->set_power)

                host->pdata->set_power(ios->power_mode, ios->vdd);

    

            break;

    }


    //设置SDI波特率预定标器寄存器以确定时钟,看其定义部分

    s3cmci_set_clk(host, ios);


    //根据SDI当前的时钟频率来设置寄存器的使能时钟位

    if (ios->clock)

        mci_con |= S3C2410_SDICON_CLOCKTYPE;

    else

        mci_con &= ~S3C2410_SDICON_CLOCKTYPE;


    //将计算好的值写回SDI控制寄存器

    writel(mci_con, host->base + S3C2410_SDICON);


    //下面只是一些调试信息,可以不要

    if ((ios->power_mode == MMC_POWER_ON) || (ios->power_mode == MMC_POWER_UP)) 

    {

        dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).n",

            host->real_rate/1000, ios->clock/1000);

    } 

    else 

    {

        dbg(host, dbg_conf, "powered down.n");

    }


    //设置总线宽度

    host->bus_width = ios->bus_width;

}


//设置SDI波特率预定标器寄存器以确定时钟

static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios)

{

    u32 mci_psc;


    //根据SDI工作时钟频率范围来确定时钟预分频器

    for (mci_psc = 0; mci_psc < 255; mci_psc++) 

    {

        host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));


        if (host->real_rate <= ios->clock)

            break;

    }


    //根据数据手册描述,SDI波特率预定标器寄存器只有8个位,所以最大值为255

    if (mci_psc > 255)

        mci_psc = 255;


    host->prescaler = mci_psc;//确定的预分频器值

    

    //将预分频器值写于SDI波特率预定标器寄存器中

    writel(host->prescaler, host->base + S3C2410_SDIPRE);


    if (ios->clock == 0)

        host->real_rate = 0;

}


MMC/SD请求处理,这是Host驱动中比较重要的一部分。请求处理的整个流程请参考(一)中的流程图,他很好的描述了一个请求是怎样从Host层发出,通过Core层提交到Card层被块设备处理的。下面看代码:


static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)

{

    //从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的

    struct s3cmci_host *host = mmc_priv(mmc);


    //s3cmci_host结构体定义的status主要是记录请求过程所处的阶段及状态,方便调试时使用

    host->status = "mmc request";

    //请求处理主要包括MMC/SD命令和数据处理,所以定义cmd_is_stop来区分是哪种请求

    host->cmd_is_stop = 0;

    //将Core层的mmc_request对象保存到Host层中以备使用

    host->mrq = mrq;


    //在开始发出一个请求前先要检测一下卡是否还存在,否则提交到了块设备层而没有请求处理的对象发生错误

    if (s3cmci_card_present(mmc) == 0) 

    {

        dbg(host, dbg_err, "%s: no medium presentn", __func__);

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

        mmc_request_done(mmc, mrq);//如果卡不存在则马上结束这次请求

    } 

    else

    {

        s3cmci_send_request(mmc);//如果卡还存在则发出请求

    }

}


//发送请求

static void s3cmci_send_request(struct mmc_host *mmc)

{

    //从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的

    struct s3cmci_host *host = mmc_priv(mmc);

    //取出在s3cmci_request函数中保存的mmc_request对象以使用

    struct mmc_request *mrq = host->mrq;

[1] [2] [3] [4] [5]
关键字:S3C2440  MMC  SD卡  驱动分析 引用地址:S3C2440上MMC/SD卡驱动分析(二)

上一篇:mini2440上dm9000驱动分析(一)
下一篇:【ARM学习笔记】ARM中断嵌套模式理解

推荐阅读

         苹果为月底的发布会设计了多少logo? 这有371个1/104                 查看原图图集模式        上周苹果发出邀请函,宣布将于本月30日在美国纽约召开新品发布会,这次的邀请函很特别, 每家媒体收到的都不太相同,每次刷新官网的图案也不一样。苹果一共为这次发布会设计了300多个不同的logo商标,并且至今...
已亲测,可以实现。第一步:配置相关管脚输出,比如我的是配置到PB9,使用的是定时器4的第四通道。void Pwm_Gpio_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); /*GPIOB_Pin_9, TIM4_Channel4 PWM*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = G...
影评家兼编剧 Drew McWeeny 在推特上宣称,苹果正寻求为其视频流媒体服务 Apple TV + 购买即将上映的詹姆斯·邦德电影《007:无暇赴死》的版权。McWeeny 暗示,Apple TV‌ + 和 Netflix 是可能播出这部新电影的两大竞争者,他认为所涉及的竞标价格很高。苹果此前已经获得了一些本应在电影院首映的电影版权,比如汤姆·汉克斯主演的《灰猎犬号...
Modbus通信协议的特点Modbus协议是应用于电子控制器上的一种通用语言。 通过此协议,控制器之间,控制器经由网络和其他设备之间可进行通信。不同厂商生产的控制设备可以连成工业网络,进行集中控制,这已成为了一种工业标准。该协议定义了控制器能够认识和使用的数据结构,描述了控制器请求访问其他设备的过程,回应其他设备的请求的方式,侦测错误记录的...

史海拾趣

问答坊 | AI 解惑

刚接触Wince,问个很低级的问题,请求大家的帮助

本人刚接触wince,现在要用EVC做应用层的开发,现在我的目的是要把一些图片放到rom里,然后再把这些图片解码出来,我有两个问题要问大家: 1.这些图片怎么放到rom里? 2.我用模拟器开发时,把这些图片放到哪个目录下才能访问的到这些图片?也就是 ...…

查看全部问答∨

请教:WINCE大容量SD卡的问题!

大家好,我现在用S3C2440A,数据手册上写了:SD Host interface version 1.0 & MMC Protocol version 2.11 compatible,这个说明它支持4G的SD卡吗?要怎么样才能使WINCE支持这4G的SD卡呀?有哪位兄弟成功过呀? !…

查看全部问答∨

WINCE 5.0 安装错误

大家好,我在xp下安装WINCE,当进行到输入product key时,输入正确的key,总是提示不正确,要求重新输入, key和安装文件都是正确的,这种问题怎么解决啊? …

查看全部问答∨

vc新手问个工程方法的问题

学了一年的VC老用不上,于是想在公司的设备基础上做个工控啥的。 思想是这样,利用串口通讯实现电脑与单片机相连,并且可以在电脑上实时监控和操作。 我想把这些工作分成通讯、数据处理、参数设定、实时监控等模块,然后编一个总界面来调这些模块 ...…

查看全部问答∨

让人惊叹的LED菊花台灯

从外形上看相信你已经对它十分感兴趣了,一种设计十分新颖的灯具——LED 菊花台灯。它的设计足够让人惊叹了,不点亮时形如洁白无瑕的菊花环绕的菊花台,是一个完美的家居装饰。当夜幕降临,华灯初上,菊花台灯一触即开,宛如千瓣菊花于黑暗中同时怒放,光 ...…

查看全部问答∨

嵌入式AES加密的应用?

在StellarisWare的软件包中都附带了aes的加解密例程,本人很好奇也研究了一下。程序中只对一个字符串进行了加解密并显示。但是不是很清除AES在嵌入式中有何具体的应用?…

查看全部问答∨

STM32新手学习系列-stm32库函数配置(转载)

STM32库函数配置stm32 固件库V3.0以上的版本,main等源文件中不再直接包含stm32f10x_conf.h,而是stm32f10x.h,stm32f10x.h则定义了启动设置,以及所有寄存器宏定义,此文件中需要注意的有: 使用V3.0以上版本固件库的方法如下: 1.选择device( ...…

查看全部问答∨

CAN控制器是否有带有DMA接口的?

请教CAN控制器是否有带有DMA接口的? 也就是说CAN数据帧收发使用DMA模式,burst模式/single模式~~~ 各位有见过这样的产品么?…

查看全部问答∨

求写hc sr04 超声波测距用Nokia5110显示的程序

求大神帮忙写一个hc sr04 超声波测距用Nokia5110显示屏显示的程序,谢谢…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved