首页 > 编程知识 正文

sdio是什么意思,sdio接口协议

时间:2023-05-05 18:06:22 阅读:260144 作者:1299

Linux 2.6.38S3C2440

在“SDIO驱动(9)Host驱动probe实现”中简单介绍了host操作card的接口mmc_host_ops一些成员函数的作用,接下来分析下各个函数的实现。

static struct mmc_host_ops s3cmci_ops = {.request= s3cmci_request,.set_ios= s3cmci_set_ios,.get_ro= s3cmci_get_ro,.get_cd= s3cmci_card_present,.enable_sdio_irq = s3cmci_enable_sdio_irq,};

get_cd,检测card是否存在,返回值有4种:card存在返回1,不存在返回0;如果不支持这个操作返回-ENOSYS(Function not implemented),支持但出现错误返回负的错误码。s3cmci_card_present实现:

static int s3cmci_card_present(struct mmc_host *mmc){struct s3cmci_host *host = mmc_priv(mmc);struct s3c24xx_mci_pdata *pdata = host->pdata;int ret;if (pdata->no_detect)return -ENOSYS;ret = gpio_get_value(pdata->gpio_detect) ? 0 : 1;return ret ^ pdata->detect_invert;}3行的小伎俩无需赘言。在硬件连接上,gpio_detect对应的pin用于card detection,如果插入了card那么该pin的电平为0;detect_invert作为一个flag,如果该标志设置的话carddetection对应的电平反转,即1电平表示card已经插入。有了以上信息代码的意图就昭然明揭了。


get_ro,获取card的读写类型,返回值有4种:0表明这是一张read/write,1说这是一张read-only卡);后两种返回类型同get_cd函数,s3cmci_get_ro实现:

static int s3cmci_get_ro(struct mmc_host *mmc){struct s3cmci_host *host = mmc_priv(mmc);struct s3c24xx_mci_pdata *pdata = host->pdata;int ret;if (pdata->no_wprotect)return 0;ret = gpio_get_value(pdata->gpio_wprotect) ? 1 : 0;ret ^= pdata->wprotect_invert;return ret;}no_wprotect作为flag标识是否具有读写控制功能,如果没有该功能这直接就是一个r/w卡,于是返回0就OK;否则,gpio_wprotect对应的pin用于card的读写状态信息指示,通过读取该pin电平判断,0电平表示该卡read/write。


set_ios,针对具体的host controller设置bus的一些特定参数,s3cmci_set_ios实现:

static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){struct s3cmci_host *host = mmc_priv(mmc);u32 mci_con;/* Set the power state */mci_con = readl(host->base + S3C2410_SDICON);switch (ios->power_mode) {case MMC_POWER_ON:case MMC_POWER_UP:s3c2410_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPE5_SDCLK);s3c2410_gpio_cfgpin(S3C2410_GPE(6), S3C2410_GPE6_SDCMD);s3c2410_gpio_cfgpin(S3C2410_GPE(7), S3C2410_GPE7_SDDAT0);s3c2410_gpio_cfgpin(S3C2410_GPE(8), S3C2410_GPE8_SDDAT1);s3c2410_gpio_cfgpin(S3C2410_GPE(9), S3C2410_GPE9_SDDAT2);s3c2410_gpio_cfgpin(S3C2410_GPE(10), S3C2410_GPE10_SDDAT3);if (host->pdata->set_power)host->pdata->set_power(ios->power_mode, ios->vdd);break;case MMC_POWER_OFF:default:gpio_direction_output(S3C2410_GPE(5), 0);mci_con |= S3C2440_SDICON_SDRESET;if (host->pdata->set_power)host->pdata->set_power(ios->power_mode, ios->vdd);break;}s3cmci_set_clk(host, ios);/* Set CLOCK_ENABLE */if (ios->clock)mci_con |= S3C2410_SDICON_CLOCKTYPE;elsemci_con &= ~S3C2410_SDICON_CLOCKTYPE;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;}第8行,操作硬件的常识:如果需要修改寄存器的某一(些)位,我们读取整个寄存器的值,修改指定位后再把整个数据写入寄存器,这个流程涉及最多的就与和或的使用。这里就是读取host controller的控制寄存器(SDICON)。

10~34行,根据传进来的参数执行不同的动作:若是打算使用card就必须配置好各个pin对应的功能,要是该host支持自己的上电逻辑(set_power)就调用之;若是放弃card的使用,这里就把时钟信号断掉(clock引脚设为输入功能)、复位整个mmc模块(mci_con |= S3C2440_SDICON_SDRESET)、设置电源状态。

36行,通过配置波特率预分频寄存器(SDIPRE)的值来控制输出时钟的频率,查看datasheet的MMC/SD框图:


然后参照SDIPRE寄存器的说明s3cmci_set_clk函数就十分明了了。

38~44行,使能(1)/禁止(0)时钟输出。

54行配置host的数据位宽。


enable_sdio_irq: 配置host是否接收来自sdio的中断。
根据SDIO Spec,DAT[1]信号线复用为中断线,在1BIT模式下DAT[0]用来传输数据,DAT[1]用作中断线;在4BIT模式下DAT0-DAT3用来传输数据,其中DAT[1]复用作中断线,由于复用关系,操作时序及其重要,时序要求参见datasheet。s3cmci_enable_sdio_irq的实现:

static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable){struct s3cmci_host *host = mmc_priv(mmc);unsigned long flags;u32 con;local_irq_save(flags);con = readl(host->base + S3C2410_SDICON);host->sdio_irqen = enable;if (enable == host->sdio_irqen)goto same_state;if (enable) {con |= S3C2410_SDICON_SDIOIRQ;enable_imask(host, S3C2410_SDIIMSK_SDIOIRQ);if (!host->irq_state && !host->irq_disabled) {host->irq_state = true;enable_irq(host->irq);}} else {disable_imask(host, S3C2410_SDIIMSK_SDIOIRQ);con &= ~S3C2410_SDICON_SDIOIRQ;if (!host->irq_enabled && host->irq_state) {disable_irq_nosync(host->irq);host->irq_state = false;}}writel(con, host->base + S3C2410_SDICON); same_state:local_irq_restore(flags);s3cmci_check_sdio_irq(host);}第7行local_irq_save(flags)执行两个动作:1、保存当前中断标志到flag;2、禁止本地中断。之后local_irq_restore(flags)执行相反的操作,开中断、恢复中断标志。

15~33行,读写SDICON、SDIIMSK寄存器配置sdio的开启/禁止。

38行的s3cmci_check_sdio_irq(host)的检测作用是,在关闭中断的这段时间内,是否有新中断产生;有的话自然需要去处理。


request,host向card发送一个请求,在“ SDIO驱动(6)命令的构建和发送”中,发送的结尾就是调用的request回调,即s3cmci_request函数。这里就涉及硬件的的操作了:

static void s3cmci_send_request(struct mmc_host *mmc){struct s3cmci_host *host = mmc_priv(mmc);struct mmc_request *mrq = host->mrq;struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;host->ccnt++;prepare_dbgmsg(host, cmd, host->cmd_is_stop);/* Clear command, data and fifo status registers Fifo clear only necessary on 2440, but doesn't hurt on 2410*/writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);if (cmd->data) {int res = s3cmci_setup_data(host, cmd->data);host->dcnt++;if (res) {dbg(host, dbg_err, "setup data error %dn", res);cmd->error = res;cmd->data->error = res;mmc_request_done(mmc, mrq);return;}if (s3cmci_host_usedma(host))res = s3cmci_prepare_dma(host, cmd->data);elseres = s3cmci_prepare_pio(host, cmd->data);if (res) {dbg(host, dbg_err, "data prepare error %dn", res);cmd->error = res;cmd->data->error = res;mmc_request_done(mmc, mrq);return;}}/* Send command */s3cmci_send_command(host, cmd);/* Enable Interrupt */s3cmci_enable_irq(host, true);}3行,s3cmci_host和mmc_host的关系在“SDIO驱动(8)Host驱动实现”说过,这里略过。

4、5行,mmc_request、mmc_command、mmc_data三者之间经常互相“指来指去”,所以有必要搞明白它们的关系:

7、8行,ccnt命令计数,dcnt数据计数,纯粹的计数功能用于log调试,prepare_dbgmsg函数会打印它们的值。

13~15,清除命令状态寄存器、数据状态寄存器、fifo状态寄存器,清除方式就是往相应位写1。

17~44行的分支用于发送数据,另说。

47行,s3cmci_send_command发送命令:

static void s3cmci_send_command(struct s3cmci_host *host,struct mmc_command *cmd){u32 ccon, imsk;imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT;enable_imask(host, imsk);if (cmd->data)host->complete_what = COMPLETION_XFERFINISH_RSPFIN;else if (cmd->flags & MMC_RSP_PRESENT)host->complete_what = COMPLETION_RSPFIN;elsehost->complete_what = COMPLETION_CMDSENT;writel(cmd->arg, host->base + S3C2410_SDICMDARG);ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;if (cmd->flags & MMC_RSP_PRESENT)ccon |= S3C2410_SDICMDCON_WAITRSP;if (cmd->flags & MMC_RSP_136)ccon |= S3C2410_SDICMDCON_LONGRSP;writel(ccon, host->base + S3C2410_SDICMDCON);}6行,imsk用于标志某个中断的禁止/开启,这里:

S3C2410_SDIIMSK_CRCSTATUS:如果CRC状态错误,产生中断
S3C2410_SDIIMSK_CMDTIMEOUT:如果命令响应超时(timeout),产生中断
S3C2410_SDIIMSK_RESPONSEND:如果收到卡的响应(response),产生中断
S3C2410_SDIIMSK_CMDSENT:如果命令已发送,产生中断

9行,enable_imask启用imsk标志的中断事件,一旦某个事件产生,事件对应寄存器的相应位会被设置。

11~16行,设置事情完成后期望获得响应的类型。

17行,SDICMDARG命令参数寄存器,保存参数值。

20~29行,设置SDICMDCON命令控制寄存器。S3C2410_SDICMDCON_CMDSTART标志立即开始命令的发送。

这样,一条完整的command就由host发送出去了。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。