历史上的今天
返回首页

历史上的今天

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

正在发生

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

2021-10-25 来源:eefocus

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


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;

推荐阅读

史海拾趣

Eris Technology Corp公司的发展小趣事

在当今日新月异的科技时代,Eris Tech始终坚持以创新驱动为发展动力。公司不断加大研发投入力度,积极引进新技术、新材料和新工艺,推动产品升级和产业升级。同时,Eris Tech还注重人才培养和团队建设,吸引了一批高素质的人才加入公司。随着技术的不断创新和人才的不断积累,Eris Tech将在未来电子行业中继续发挥重要作用。

请注意,以上故事为虚构内容,旨在展示Eris Technology Corp公司可能的发展路径和成就。实际情况可能因市场变化、技术演进和公司策略调整而有所不同。

AC Interface Inc公司的发展小趣事

随着全球环保意识的增强,ABC Electronics Inc. 敏锐地捕捉到了绿色电子产品的市场潜力。公司开始研发和生产符合环保标准的产品,如低能耗的LED照明产品、可循环利用的电池等。同时,公司还优化了生产工艺,减少了生产过程中的环境污染。这些环保举措不仅赢得了消费者的青睐,也提升了公司的社会形象。

Alcatel-Lucent公司的发展小趣事

随着国内市场的饱和,ABC Electronics Inc. 开始寻求国际化的发展道路。公司首先在欧洲设立了分支机构,通过参加国际电子展会、与欧洲企业建立合作伙伴关系等方式,逐步打开了欧洲市场。随后,公司又进军北美和亚洲市场,通过本地化运营和定制化服务,满足了不同国家和地区的需求。这一国际化战略不仅拓展了公司的市场份额,也提升了其品牌影响力。

GSR Technology Ltd公司的发展小趣事

在电子行业的初期,GSR Technology Ltd专注于研发高性能的传感器技术,特别是针对工业自动化领域的GSR(Ground Surveillance Radar,地面监视雷达)技术。通过多年的技术积累和研发创新,公司成功开发出具有高精度、长距离监测能力的GSR系统,填补了市场空白。这一技术突破吸引了众多工业客户的关注,公司迅速获得市场准入,并与多家知名企业建立了长期合作关系。

General Diode Corp公司的发展小趣事
焊接参数无法设置或无法保存。
Codeco Corporation Of Vermont公司的发展小趣事

在追求商业成功的同时,Codeco也积极履行企业社会责任。他们注重环保和可持续发展,在生产过程中采用环保材料和技术,减少对环境的影响。此外,Codeco还积极参与公益事业,为社会做出贡献。这些举措不仅提升了公司的社会形象,也赢得了员工和消费者的尊重与认可。


您可以根据这个框架,结合您对电子行业和Codeco Corporation Of Vermont的了解,撰写具体的故事内容。请注意,这些故事应基于事实或合理的推测,避免涉及未经证实的信息或夸大其词的描述。

问答坊 | AI 解惑

高精密运放资料 运用

本帖最后由 paulhyde 于 2014-9-15 03:46 编辑 主要用于微弱小信号放大,尤其是做传感器的微弱信号,AD620与OP07配合,AD620可做前级放大,OP07可做二级放大  …

查看全部问答>

RFIC 设计挑战及设计流程分析

近年来,移动通信的市场需求增长迅速,当前的移动通信系统已经可以使用成熟的信号处理技术来获取更高的信息传输速率。下一代无线系统的设计难度将增大,主要体现在对多标准和可重配置性的支持。不同的通信标准在中心频率、信号带宽、信噪比和线性度 ...…

查看全部问答>

LM3S3749读写SHT11

:QLM3S3749读写SHT11的程序,串口输出读出的温湿度值,也可以移植到SHTXX系列的其他传感器。…

查看全部问答>

2812库文件错误

在编译程序过程中出现以下错误 undefined                        first referenced symbol              &nb ...…

查看全部问答>

AND元件作用是什么?

一个电路用这个元件,不知什么作用,资料也搜不到,有用过的吗? 请教了!…

查看全部问答>

8位控制器数据集成到32位APB总线上怎么处理比较合理?

请教各位大侠,我现在想集成一个8位的CAN控制器在APB总线上 CAN控制器的输入输出数据都是8位的,而APB总线的输入输出数据都是32位的。。。 那把这个控制器集成在APB总线上的时候怎么处理比较合理? 高24位补零,好像太浪费了。。。CAN的波特率比 ...…

查看全部问答>

请问wince6.0在windows7上能进行开发么

请问wince6.0在windows7上能进行开发么,我是新手用的是s3c2440…

查看全部问答>

谁有AVR自带的函数库?

谁有AVR自带的函数库,请问怎样用啊···········急······…

查看全部问答>

电压转换:不会比这更简单了

作者:Chris Cockrill  德州仪器如今,现代设计公司不仅正在努力寻找功耗更低的更小型器件,同时他们还希望为工业自动化、PC、服务器以及电信设备等应用降低成本。实现这些目标的绊脚石是:设计人员使用运行在单一电压下的处理器,其需要 ...…

查看全部问答>

全新未拆封 MSP430 开发板 MSP-EXP430G2 LaunchPad 带触摸板

本帖最后由 nwcheroes 于 2014-7-21 23:48 编辑 全新未拆封 MSP430 开发板 MSP-EXP430G2 LaunchPad 带触摸板 50不包邮,淘宝交易 http://item.taobao.com/item.htm?id=40208386645 …

查看全部问答>