前面分析了SDIO探测原理,现在我们来看下SDIO读写传输。SDIO提供了两个新的I/O读写命令: CMD52(IO_RW_DIRECT)和CMD53(IO_RW_EXTENDED)。CMD52用来读写一个字节,CMD53用来读写更多的数据。

CMD53数据传输分块传输(block mode)和字节传输(byte mode)两种模式,下表是与SD memory card传输格式对比:

SDIO card SD memory card
CMD53 rd (byte) CMD17 (READ_SINGLE_BLOCK)
CMD53 wr (byte) CMD24 (WRITE_BLOCK)
CMD53 rd (blk) CMD18 (READ_MULTIPLE_BLOCK)
CMD53 wr (blk) CMD25 (WRITE_MULTIPLE_BLOCK)

SDIO的一个feature就是可以interrupt host,前提是使能card的相应功能中断(CCCR的IENx/IENM)。host检测到这个pending中断后(CCCR的INTx)后会做一些特定处理。

看下CCCR(common control registers)相关寄存器定义 :

sdio-cccr.png

Linux Kernel传输分析

参考5.x:

sdio_claim_irq()会使能中断:

/**
* sdio_claim_irq - claim the IRQ for a SDIO function
* @func: SDIO function
* @handler: IRQ handler callback
*
* Claim and activate the IRQ for the given SDIO function. The provided
* handler will be called when that IRQ is asserted. The host is always
* claimed already when the handler is called so the handler should not
* call sdio_claim_host() or sdio_release_host().
*/
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
{
int ret;
unsigned char reg;

if (!func)
return -EINVAL;

pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));

if (func->irq_handler) {
pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
return -EBUSY;
}

ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
if (ret)
return ret;

reg |= 1 << func->num;

reg |= 1; /* Master interrupt enable */

ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL); //tj: enable int
if (ret)
return ret;

func->irq_handler = handler; //tj: 注册isr
ret = sdio_card_irq_get(func->card);
if (ret)
func->irq_handler = NULL;
sdio_single_irq_set(func->card);

return ret;
}
EXPORT_SYMBOL_GPL(sdio_claim_irq);
#define SDIO_CCCR_IENx                0x04    /* Function/Master Interrupt Enable */

call mmc_io_rw_direct()使能此func的中断并挂接中断处理func->irq_handler = handler。然后使能host侧中断:

static int sdio_card_irq_get(struct mmc_card *card)
{
struct mmc_host *host = card->host;

WARN_ON(!host->claimed);

if (!host->sdio_irqs++) {
if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
atomic_set(&host->sdio_irq_thread_abort, 0);
host->sdio_irq_thread =
kthread_run(sdio_irq_thread, host,
"ksdioirqd/%s", mmc_hostname(host));
if (IS_ERR(host->sdio_irq_thread)) {
int err = PTR_ERR(host->sdio_irq_thread);
host->sdio_irqs--;
return err;
}
} else if (host->caps & MMC_CAP_SDIO_IRQ) {
host->ops->enable_sdio_irq(host, 1);
}
}

return 0;
}

我们看下具备MMC_CAP_SDIO_IRQ能力会走一个host的callback操作(->enable_sdio_irq)。对标准的SD host controller,这个callback定义在sdhci.c:

static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.post_req = sdhci_post_req,
.pre_req = sdhci_pre_req,
.set_ios = sdhci_set_ios,
.get_cd = sdhci_get_cd,
.get_ro = sdhci_get_ro,
.hw_reset = sdhci_hw_reset,
.enable_sdio_irq = sdhci_enable_sdio_irq, //tj: here

sdhci_enable_sdio_irq()会call sdhci_enable_sdio_irq_nolock():

static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
{
if (!(host->flags & SDHCI_DEVICE_DEAD)) {
if (enable)
host->ier |= SDHCI_INT_CARD_INT;
else
host->ier &= ~SDHCI_INT_CARD_INT;

sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
}
}
#define SDHCI_INT_ENABLE        0x34
#define SDHCI_SIGNAL_ENABLE 0x38
...
#define SDHCI_INT_CARD_INT 0x00000100

host spec有如下定义:

sdio-host-34h-reg.png

sdio-host-38h-reg.png

clear? ok.

看下isr call stack:

sdhci_irq() -> sdio_signal_irq() with SDHCI_INT_CARD_INT -> queue_delayed_work()
sdio_irq_work() -> sdio_run_irqs() -> process_sdio_pending_irqs()
static int process_sdio_pending_irqs(struct mmc_host *host)
{
...
ret = sdio_get_pending_irqs(host, &pending);
if (ret)
return ret;

count = 0;
for (i = 1; i <= 7; i++) {
if (pending & (1 << i)) {
func = card->sdio_func[i - 1];
if (!func) {
pr_warn("%s: pending IRQ for non-existent function\n",
mmc_card_id(card));
ret = -EINVAL;
} else if (func->irq_handler) {
func->irq_handler(func);
count++;
} else {
pr_warn("%s: pending IRQ with no handler\n",
sdio_func_id(func));
ret = -EINVAL;
}
}
}
}

先是获取pending:


static int sdio_get_pending_irqs(struct mmc_host *host, u8 *pending)
{
struct mmc_card *card = host->card;
int ret;

WARN_ON(!host->claimed);

ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, pending);
if (ret) {
pr_debug("%s: error %d reading SDIO_CCCR_INTx\n",
mmc_card_id(card), ret);
return ret;
}

然后如果有pending(pending & (1 << i)),走callback(irq_handler),也就是之前在sdio_claim_irq()注册的handler。

sdio_io.c主要是单字节读写传输接口:

*      sdio_readb - read a single byte from a SDIO function
* sdio_writeb - write a single byte to a SDIO function

就是封装了mmc_io_rw_direct()

多数据传输:

*      sdio_readsb - read from a FIFO on a SDIO function
* sdio_writesb - write to a FIFO of a SDIO function

一样的数据切割处理最后call mmc_io_rw_extended( ..., blocks, blksz)

Done.