Linux驱动——sdio type card(八)

备注:
  1. Kernel版本:5.4
  2. 使用工具:Source Insight 4.0

文章目录

  • Linux驱动——sdio type card(八)
    • 概述
    • 数据结构
      • mmc_sdio_ops
    • 核心接口说明
      • sdio type card匹配相关
        • mmc_attach_sdio
        • mmc_sdio_init_card
      • sdio ops相关函数
        • mmc_send_io_op_cond
        • mmc_io_rw_direct
        • mmc_io_rw_extended
        • sdio_reset
      • sdio irq相关函数
        • 注册sdio_irq——sdio_claim_irq
        • 释放sdio_irq——sdio_release_irq
        • 创建sdio_irq_thread——sdio_card_irq_get
        • 实现sdio_irq_thread——sdio_irq_thread
      • sdio io相关函数
        • host相关
        • sdio func相关
        • 块配置相关
        • IO读写相关

概述

  card相关模块为对应card实现相应的操作,包括初始化操作、以及对应的总线操作集合。负责和对应card协议层相关的东西。
sdio type card相关代码:

drivers/mmc/core/sdio.c(提供接口)
drivers/mmc/core/sdio_ops.c(提供和sdio type card协议相关的操作)
drivers/mmc/core/sdio_irq.c(提供sdio irq相关功能的操作)
drivers/mmc/core/sdio_cis.c(提供sdio cis相关功能的操作)
drivers/mmc/core/sdio_ops.h

数据结构

mmc_sdio_ops

static const struct mmc_bus_ops mmc_sdio_ops = {.remove = mmc_sdio_remove,.detect = mmc_sdio_detect,.pre_suspend = mmc_sdio_pre_suspend,.suspend = mmc_sdio_suspend,.resume = mmc_sdio_resume,.runtime_suspend = mmc_sdio_runtime_suspend,.runtime_resume = mmc_sdio_runtime_resume,.alive = mmc_sdio_alive,.hw_reset = mmc_sdio_hw_reset,.sw_reset = mmc_sdio_sw_reset,
};

核心接口说明

sdio type card匹配相关

mmc_attach_sdio

/** Starting point for SDIO card init.*/
int mmc_attach_sdio(struct mmc_host *host)
{int err, i, funcs;u32 ocr, rocr;struct mmc_card *card;WARN_ON(!host->claimed);/** * 以下部分,连同mmc_rescan_try_freq中的* mmc_go_idle和mmc_send_if_cond一起构成了* “尝试获取一个合适的工作电压” 的任务 */// host发送参数为0的ACMD41命令,提取response中的VHS,// 得到card支持的工作电压范围err = mmc_send_io_op_cond(host, 0, &ocr);if (err)return err;//创建sd_ops,host->bus_ops指向mmc_sdio_opsmmc_attach_bus(host, &mmc_sdio_ops);if (host->ocr_avail_sdio)host->ocr_avail = host->ocr_avail_sdio;// host选择一个card和host都支持的最低的工作电压,// 并将host提供给card的工作电压设置为这个值。// 后续就以host->ocr作为工作电压对sd card进行初始化rocr = mmc_select_voltage(host, ocr);/** Can we support the voltage(s) of the card(s)?*/if (!rocr) {err = -EINVAL;goto err;}/** Detect and init the card.*//** 上述已经完成了card的识别操作,并且为card选择了一个合适的工作电压 **//** 后续调用mmc_sdio_init_card对sdio card进行初始化,也就是代码核心 **/err = mmc_sdio_init_card(host, rocr, NULL);if (err)goto err;card = host->card;/** Enable runtime PM only if supported by host+card+board*/if (host->caps & MMC_CAP_POWER_OFF_CARD) {/** Do not allow runtime suspend until after SDIO function* devices are added.*/pm_runtime_get_noresume(&card->dev);/** Let runtime PM core know our card is active*/err = pm_runtime_set_active(&card->dev);if (err)goto remove;/** Enable runtime PM for this card*/pm_runtime_enable(&card->dev);}/** The number of functions on the card is encoded inside* the ocr.*/// 根据ocr寄存器获取func数目funcs = (ocr & 0x70000000) >> 28;card->sdio_funcs = 0;/** Initialize (but don't add) all present functions.*/// 初始化sdio_funcfor (i = 0; i < funcs; i++, card->sdio_funcs++) {err = sdio_init_func(host->card, i + 1);if (err)goto remove;/** Enable Runtime PM for this func (if supported)*/if (host->caps & MMC_CAP_POWER_OFF_CARD)pm_runtime_enable(&card->sdio_func[i]->dev);}/** First add the card to the driver model...*/mmc_release_host(host);// 将sd card添加到device中err = mmc_add_card(host->card);if (err)goto remove_added;/** ...then the SDIO functions.*///注册 sdio_func 到driver中for (i = 0;i < funcs;i++) {err = sdio_add_func(host->card->sdio_func[i]);if (err)goto remove_added;}if (host->caps & MMC_CAP_POWER_OFF_CARD)pm_runtime_put(&card->dev);mmc_claim_host(host);return 0;remove:mmc_release_host(host);
remove_added:/** The devices are being deleted so it is not necessary to disable* runtime PM. Similarly we also don't pm_runtime_put() the SDIO card* because it needs to be active to remove any function devices that* were probed, and after that it gets deleted.*/mmc_sdio_remove(host);mmc_claim_host(host);
err:mmc_detach_bus(host);pr_err("%s: error %d whilst initialising SDIO card\n",mmc_hostname(host), err);return err;
}

mmc_sdio_init_card

/** Handle the detection and initialisation of a card.** In the case of a resume, "oldcard" will contain the card* we're trying to reinitialise.*/
static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,struct mmc_card *oldcard)
{struct mmc_card *card;int err;int retries = 10;u32 rocr = 0;u32 ocr_card = ocr;WARN_ON(!host->claimed);/* to query card if 1.8V signalling is supported */if (mmc_host_uhs(host))ocr |= R4_18V_PRESENT;try_again:/** 在mmc_sd_get_cid中完成如下工作::: **//** 重新复位,完成card的内部初始化 **//** 设置信号电压,包括card和host的设置 **//** 获取card的CID值 **/// 调用mmc_sd_get_cid进行复位、内部初始化,// 设置信号电压,然后获取CID值,// 最终card进入了identification state。if (!retries) {pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));ocr &= ~R4_18V_PRESENT;}/** Inform the card of the voltage*/err = mmc_send_io_op_cond(host, ocr, &rocr);if (err)return err;/** For SPI, enable CRC as appropriate.*/if (mmc_host_is_spi(host)) {err = mmc_spi_set_crc(host, use_spi_crc);if (err)return err;}/** Allocate card structure.*/card = mmc_alloc_card(host, NULL);if (IS_ERR(card))return PTR_ERR(card);if ((rocr & R4_MEMORY_PRESENT) &&mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {card->type = MMC_TYPE_SD_COMBO;if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {err = -ENOENT;goto mismatch;}} else {card->type = MMC_TYPE_SDIO;if (oldcard && oldcard->type != MMC_TYPE_SDIO) {err = -ENOENT;goto mismatch;}}/** Call the optional HC's init_card function to handle quirks.*/if (host->ops->init_card)host->ops->init_card(host, card);/** If the host and card support UHS-I mode request the card* to switch to 1.8V signaling level.  No 1.8v signalling if* UHS mode is not enabled to maintain compatibility and some* systems that claim 1.8v signalling in fact do not support* it. Per SDIO spec v3, section 3.1.2, if the voltage is already* 1.8v, the card sets S18A to 0 in the R4 response. So it will* fails to check rocr & R4_18V_PRESENT,  but we still need to* try to init uhs card. sdio_read_cccr will take over this task* to make sure which speed mode should work.*/if (rocr & ocr & R4_18V_PRESENT) {err = mmc_set_uhs_voltage(host, ocr_card);if (err == -EAGAIN) {mmc_sdio_resend_if_cond(host, card);retries--;goto try_again;} else if (err) {ocr &= ~R4_18V_PRESENT;}}/** For native busses:  set card RCA and quit open drain mode.*/if (!mmc_host_is_spi(host)) {err = mmc_send_relative_addr(host, &card->rca);if (err)goto remove;/** Update oldcard with the new RCA received from the SDIO* device -- we're doing this so that it's updated in the* "card" struct when oldcard overwrites that later.*/if (oldcard)oldcard->rca = card->rca;}/** Read CSD, before selecting the card*/if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {err = mmc_sd_get_csd(host, card);if (err)goto remove;mmc_decode_cid(card);}/** Select card, as all following commands rely on that.*/if (!mmc_host_is_spi(host)) {err = mmc_select_card(card);if (err)goto remove;}if (card->quirks & MMC_QUIRK_NONSTD_SDIO) {/** This is non-standard SDIO device, meaning it doesn't* have any CIA (Common I/O area) registers present.* It's host's responsibility to fill cccr and cis* structures in init_card().*/mmc_set_clock(host, card->cis.max_dtr);if (card->cccr.high_speed) {mmc_set_timing(card->host, MMC_TIMING_SD_HS);}if (oldcard)mmc_remove_card(card);elsehost->card = card;return 0;}/** Read the common registers. Note that we should try to* validate whether UHS would work or not.*/err = sdio_read_cccr(card, ocr);if (err) {mmc_sdio_resend_if_cond(host, card);if (ocr & R4_18V_PRESENT) {/* Retry init sequence, but without R4_18V_PRESENT. */retries = 0;goto try_again;}return err;}/** Read the common CIS tuples.*/// 获取card CIS 值err = sdio_read_common_cis(card);if (err)goto remove;if (oldcard) {if (card->cis.vendor == oldcard->cis.vendor &&card->cis.device == oldcard->cis.device) {mmc_remove_card(card);card = oldcard;} else {err = -ENOENT;goto mismatch;}}card->ocr = ocr_card;mmc_fixup_device(card, sdio_fixup_methods);/** 获取sd card的配置寄存器和状态寄存器 **//** 读取card 的switch状态,也就是其支持的function **/// host发送ACMD51命令,要求card回复其SCR寄存器(SD configuration register)的值// host发送ACMD13命令,要求card回复其SSR寄存器(SD status regiter)的值// host发送CMD6命令来读取card switch status。// 通过card switch status可以得到card支持的总线速度模式以及驱动强度类型。if (card->type == MMC_TYPE_SD_COMBO) {err = mmc_sd_setup_card(host, card, oldcard != NULL);/* handle as SDIO-only card if memory init failed */if (err) {mmc_go_idle(host);if (mmc_host_is_spi(host))/* should not fail, as it worked previously */mmc_spi_set_crc(host, use_spi_crc);card->type = MMC_TYPE_SDIO;} elsecard->dev.type = &sd_type;}/** If needed, disconnect card detection pull-up resistor.*/// 关闭 card detection 功能err = sdio_disable_cd(card);if (err)goto remove;/* Initialization sequence for UHS-I cards *//* Only if card supports 1.8v and UHS signaling */// 切换到sdio highspeed 模式、配置时钟速率、配置数据总线宽度if ((ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode) {err = mmc_sdio_init_uhs_card(card);if (err)goto remove;} else {/** Switch to high-speed (if supported).*/err = sdio_enable_hs(card);if (err > 0)mmc_set_timing(card->host, MMC_TIMING_SD_HS);else if (err)goto remove;/** Change to the card's maximum speed.*/mmc_set_clock(host, mmc_sdio_get_max_clock(card));/** Switch to wider bus (if supported).*/err = sdio_enable_4bit_bus(card);if (err)goto remove;}if (host->caps2 & MMC_CAP2_AVOID_3_3V &&host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) {pr_err("%s: Host failed to negotiate down from 3.3V\n",mmc_hostname(host));err = -EINVAL;goto remove;}host->card = card;return 0;mismatch:pr_debug("%s: Perhaps the card was replaced\n", mmc_hostname(host));
remove:if (oldcard != card)mmc_remove_card(card);return err;
}

sdio ops相关函数

mmc_send_io_op_cond

int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{struct mmc_command cmd = {};int i, err = 0;cmd.opcode = SD_IO_SEND_OP_COND;cmd.arg = ocr;cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;for (i = 100; i; i--) {err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);if (err)break;/* if we're just probing, do a single pass */if (ocr == 0)break;/* otherwise wait until reset completes */if (mmc_host_is_spi(host)) {/** Both R1_SPI_IDLE and MMC_CARD_BUSY indicate* an initialized card under SPI, but some cards* (Marvell's) only behave when looking at this* one.*/if (cmd.resp[1] & MMC_CARD_BUSY)break;} else {if (cmd.resp[0] & MMC_CARD_BUSY)break;}err = -ETIMEDOUT;mmc_delay(10);}if (rocr)*rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0];return err;
}

mmc_io_rw_direct

int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,unsigned addr, u8 in, u8 *out)
{return mmc_io_rw_direct_host(card->host, write, fn, addr, in, out);
}
static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,unsigned addr, u8 in, u8 *out)
{struct mmc_command cmd = {};int err;if (fn > 7)return -EINVAL;/* sanity check */if (addr & ~0x1FFFF)return -EINVAL;cmd.opcode = SD_IO_RW_DIRECT;cmd.arg = write ? 0x80000000 : 0x00000000;cmd.arg |= fn << 28;cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;cmd.arg |= addr << 9;cmd.arg |= in;cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;err = mmc_wait_for_cmd(host, &cmd, 0);if (err)return err;if (mmc_host_is_spi(host)) {/* host driver already reported errors */} else {if (cmd.resp[0] & R5_ERROR)return -EIO;if (cmd.resp[0] & R5_FUNCTION_NUMBER)return -EINVAL;if (cmd.resp[0] & R5_OUT_OF_RANGE)return -ERANGE;}if (out) {if (mmc_host_is_spi(host))*out = (cmd.resp[0] >> 8) & 0xFF;else*out = cmd.resp[0] & 0xFF;}return 0;
}

mmc_io_rw_extended

int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
{struct mmc_request mrq = {};struct mmc_command cmd = {};struct mmc_data data = {};struct scatterlist sg, *sg_ptr;struct sg_table sgtable;unsigned int nents, left_size, i;unsigned int seg_size = card->host->max_seg_size;int err;WARN_ON(blksz == 0);/* sanity check */if (addr & ~0x1FFFF)return -EINVAL;mrq.cmd = &cmd;mrq.data = &data;cmd.opcode = SD_IO_RW_EXTENDED;                 // cmd53cmd.arg = write ? 0x80000000 : 0x00000000;     // 读写方向cmd.arg |= fn << 28;                          // func->numcmd.arg |= incr_addr ? 0x04000000 : 0x00000000;cmd.arg |= addr << 9;                         // 块地址if (blocks == 0)cmd.arg |= (blksz == 512) ? 0 : blksz;   /* byte mode */elsecmd.arg |= 0x08000000 | blocks;     /* block mode */cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;data.blksz = blksz; // 配置块大小/* Code in host drivers/fwk assumes that "blocks" always is >=1 */data.blocks = blocks ? blocks : 1; // 配置块数量data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;left_size = data.blksz * data.blocks;nents = DIV_ROUND_UP(left_size, seg_size);if (nents > 1) {if (sg_alloc_table(&sgtable, nents, GFP_KERNEL))return -ENOMEM;data.sg = sgtable.sgl;data.sg_len = nents;for_each_sg(data.sg, sg_ptr, data.sg_len, i) {sg_set_buf(sg_ptr, buf + i * seg_size,min(seg_size, left_size));left_size -= seg_size;}} else {data.sg = &sg;data.sg_len = 1;//初始化sg,找到buf对应的物理页地址,为DMA传输做准备sg_init_one(&sg, buf, left_size);}mmc_set_data_timeout(&data, card);mmc_pre_req(card->host, &mrq);//发送数据,并且等待返回mmc_wait_for_req(card->host, &mrq);if (cmd.error)err = cmd.error;else if (data.error)err = data.error;else if (mmc_host_is_spi(card->host))/* host driver already reported errors */err = 0;else if (cmd.resp[0] & R5_ERROR)//返回数据处理R5err = -EIO;else if (cmd.resp[0] & R5_FUNCTION_NUMBER)err = -EINVAL;else if (cmd.resp[0] & R5_OUT_OF_RANGE)err = -ERANGE;elseerr = 0;mmc_post_req(card->host, &mrq, err);if (nents > 1)sg_free_table(&sgtable);return err;
}

sdio_reset

int sdio_reset(struct mmc_host *host)
{int ret;u8 abort;/* SDIO Simplified Specification V2.0, 4.4 Reset for SDIO */ret = mmc_io_rw_direct_host(host, 0, 0, SDIO_CCCR_ABORT, 0, &abort);if (ret)abort = 0x08;elseabort |= 0x08;return mmc_io_rw_direct_host(host, 1, 0, SDIO_CCCR_ABORT, abort, NULL);
}

sdio irq相关函数

注册sdio_irq——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));// sdio_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);if (ret)return ret;func->irq_handler = handler;// 创建sdio_irq_thread内核线程ret = sdio_card_irq_get(func->card);if (ret)func->irq_handler = NULL;// 配置sdio_single_irqsdio_single_irq_set(func->card);return ret;
}
EXPORT_SYMBOL_GPL(sdio_claim_irq);

释放sdio_irq——sdio_release_irq

/*** sdio_release_irq - release the IRQ for a SDIO function* @func: SDIO function** Disable and release the IRQ for the given SDIO function.*/
int sdio_release_irq(struct sdio_func *func)
{int ret;unsigned char reg;if (!func)return -EINVAL;pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));if (func->irq_handler) {func->irq_handler = NULL;sdio_card_irq_put(func->card);sdio_single_irq_set(func->card);}ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);if (ret)return ret;reg &= ~(1 << func->num);/* Disable master interrupt with the last function interrupt */if (!(reg & 0xFE))reg = 0;ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);if (ret)return ret;return 0;
}
EXPORT_SYMBOL_GPL(sdio_release_irq);

创建sdio_irq_thread——sdio_card_irq_get

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);//创建sdio irq thread 内核线程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;
}

实现sdio_irq_thread——sdio_irq_thread

static int sdio_irq_thread(void *_host)
{struct mmc_host *host = _host;struct sched_param param = { .sched_priority = 1 };unsigned long period, idle_period;int ret;// 将sdio irq thread线程为实时线程FIFO,优先级为1sched_setscheduler(current, SCHED_FIFO, &param);/** We want to allow for SDIO cards to work even on non SDIO* aware hosts.  One thing that non SDIO host cannot do is* asynchronous notification of pending SDIO card interrupts* hence we poll for them in that case.*/// 轮询运行时间为10msidle_period = msecs_to_jiffies(10);// 若host支持硬件sdio_irq,则为中断模式,否则为轮询模式,10ms调度一次period = (host->caps & MMC_CAP_SDIO_IRQ) ?MAX_SCHEDULE_TIMEOUT : idle_period;pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",mmc_hostname(host), period);do {/** We claim the host here on drivers behalf for a couple* reasons:** 1) it is already needed to retrieve the CCCR_INTx;* 2) we want the driver(s) to clear the IRQ condition ASAP;* 3) we need to control the abort condition locally.** Just like traditional hard IRQ handlers, we expect SDIO* IRQ handlers to be quick and to the point, so that the* holding of the host lock does not cover too much work* that doesn't require that lock to be held.*/// 获取host的使用权ret = __mmc_claim_host(host, NULL,&host->sdio_irq_thread_abort);if (ret)break;// 调用sdio_fun irq_handler函数或获取sdio card status再调用sdio_funcret = process_sdio_pending_irqs(host);// 释放host的使用权mmc_release_host(host);/** Give other threads a chance to run in the presence of* errors.*/if (ret < 0) {set_current_state(TASK_INTERRUPTIBLE);if (!kthread_should_stop())schedule_timeout(HZ);set_current_state(TASK_RUNNING);}/** Adaptive polling frequency based on the assumption* that an interrupt will be closely followed by more.* This has a substantial benefit for network devices.*/if (!(host->caps & MMC_CAP_SDIO_IRQ)) {if (ret > 0)period /= 2;else {period++;if (period > idle_period)period = idle_period;}}set_current_state(TASK_INTERRUPTIBLE);// 处理完sdio card中的数据,若支持sdio_irq,则使能sdio_irqif (host->caps & MMC_CAP_SDIO_IRQ)host->ops->enable_sdio_irq(host, 1);if (!kthread_should_stop())schedule_timeout(period);set_current_state(TASK_RUNNING);} while (!kthread_should_stop());// 支持sdio_irq,则关闭sdio_irqif (host->caps & MMC_CAP_SDIO_IRQ)host->ops->enable_sdio_irq(host, 0);pr_debug("%s: IRQ thread exiting with code %d\n",mmc_hostname(host), ret);return ret;
}

process_sdio_pending_irqs:

static int process_sdio_pending_irqs(struct mmc_host *host)
{struct mmc_card *card = host->card;int i, ret, count;bool sdio_irq_pending = host->sdio_irq_pending;unsigned char pending;struct sdio_func *func;/* Don't process SDIO IRQs if the card is suspended. */if (mmc_card_suspended(card))return 0;/* Clear the flag to indicate that we have processed the IRQ. */host->sdio_irq_pending = false;/** Optimization, if there is only 1 function interrupt registered* and we know an IRQ was signaled then call irq handler directly.* Otherwise do the full probe.*/// sdio_single_irq已注册,则直接调用func->irq_handler处理本次中断func = card->sdio_single_irq;if (func && sdio_irq_pending) {func->irq_handler(func);return 1;}// 发送cmd5 查询irq_pending???ret = sdio_get_pending_irqs(host, &pending);if (ret)return ret;// 根据pending,调用所对应的funccount = 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;}}}if (count)return count;return ret;
}

sdio_get_pending_irqs:

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;}if (*pending && mmc_card_broken_irq_polling(card) &&!(host->caps & MMC_CAP_SDIO_IRQ)) {unsigned char dummy;/* A fake interrupt could be created when we poll SDIO_CCCR_INTx* register with a Marvell SD8797 card. A dummy CMD52 read to* function 0 register 0xff can avoid this.*/mmc_io_rw_direct(card, 0, 0, 0xff, 0, &dummy);}return 0;
}

sdio io相关函数

host相关

sdio_claim_host:

void sdio_claim_host(struct sdio_func *func)
{if (WARN_ON(!func))return;mmc_claim_host(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_claim_host);

sdio_release_host:

void sdio_release_host(struct sdio_func *func)
{if (WARN_ON(!func))return;mmc_release_host(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_release_host);

sdio func相关

int sdio_enable_func(struct sdio_func *func);
int sdio_disable_func(struct sdio_func *func);

块配置相关

int sdio_set_block_size(struct sdio_func *func, unsigned blksz);
unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz);

IO读写相关

// 读写1byte
u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret);
void sdio_writeb(struct sdio_func *func, u8 b, unsigned int addr, int *err_ret);
u8 sdio_writeb_readb(struct sdio_func *func, u8 write_byte, unsigned int addr, int *err_ret);// 读写块数据
int sdio_memcpy_fromio(struct sdio_func *func, void *dst, unsigned int addr, int count);
int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr, void *src, int count);// 读写FIFO 1byte
int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, int count);
int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src, int count);// 读写16bit(2byte)
u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret);
void sdio_writew(struct sdio_func *func, u16 b, unsigned int addr, int *err_ret);// 读写32bit(4byte)
u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret);
void sdio_writel(struct sdio_func *func, u32 b, unsigned int addr, int *err_ret);// sdio func0 读取1byte
unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr, int *err_ret);
void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, int *err_ret);

  
sdio_io_rw_ext_helper:
  以上IO相关的函数,最终都是调用本函数来实现相关的功能,具体实现方式如下:

/* Split an arbitrarily sized data transfer into several* IO_RW_EXTENDED commands. */
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,unsigned addr, int incr_addr, u8 *buf, unsigned size)
{unsigned remainder = size;unsigned max_blocks;int ret;if (!func || (func->num > 7))return -EINVAL;/* Do the bulk of the transfer using block mode (if supported). */// 支持多块传输,且当前传输数据大于一块大小if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) {/* Blocks per command is limited by host count, host transfer* size and the maximum for IO_RW_EXTENDED of 511 blocks. */max_blocks = min(func->card->host->max_blk_count, 511u);// 快对齐收发数据while (remainder >= func->cur_blksize) {unsigned blocks;blocks = remainder / func->cur_blksize;if (blocks > max_blocks)blocks = max_blocks;size = blocks * func->cur_blksize;ret = mmc_io_rw_extended(func->card, write,func->num, addr, incr_addr, buf,blocks, func->cur_blksize);if (ret)return ret;remainder -= size;buf += size;if (incr_addr)addr += size;}}/* Write the remainder using byte mode. */// 收发剩余数据while (remainder > 0) {size = min(remainder, sdio_max_byte_size(func));/* Indicate byte mode by setting "blocks" = 0 */ret = mmc_io_rw_extended(func->card, write, func->num, addr,incr_addr, buf, 0, size);if (ret)return ret;remainder -= size;buf += size;if (incr_addr)addr += size;}return 0;
}

Linux驱动——sdio type card(八)相关推荐

  1. Linux驱动——sd type card(七)

    Linux驱动--sd type card(七) 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: [mmc subsyste ...

  2. Linux驱动——mmc sd card初始化流程(十一)

    Linux驱动--mmc sd card初始化流程(十一) 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客:   (1)[sd ...

  3. Linux驱动——mmc sd card 块设备读写流程(十三)

    Linux驱动--mmc sd card 块设备读写流程(十三) 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客:   (1) ...

  4. Linux驱动开发(十八)---网络(网卡)驱动学习

    前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...

  5. Linux 驱动开发 四十八:Linux INPUT 子系统实验

    一.input 子系统简介 input 就是输入的意思,因此 input 子系统就是管理输入的子系统,是 Linux 内核针对某一类设备而创建的框架. 比如按键输入.键盘.鼠标.触摸屏等等这些都属于输 ...

  6. Linux驱动开发(十八):I2C驱动

    简介 I2C是我们在单片机开发时时常会用到的通讯接口,用来与一些字符型设备进行通信,比如:陀螺仪.温度传感器等等,同样的在Linux下I2C驱动也是十分重要的.有了操作系统的加持,我们不用像在32上那 ...

  7. Linux驱动编程 step-by-step (八) 阻塞型字符设备驱动

    阻塞型字符设备驱动 前面说到了 如何实现read write 等操作,但如果设备缓冲已满,现在想而用户此时又想写入设备,此请求就无法立即执行,那怎么办呢? 第一种情况是:驱动程序想用户返回请求失败的信 ...

  8. 迅为嵌入式linux驱动开发笔记(八)—内核定时器

    内核定时器 二. Linux 内核定时器基础知识 Linux 内 核 使 用 timer_list 结 构 体表示内核定时器 ,timer_list定义在文件 include/linux/timer. ...

  9. Linux 驱动开发 二十八:读写锁

    参考博客:Linux 内核同步(三):读-写自旋锁(rwlock)_StephenZhou-CSDN博客_linux rwlock 使用 spinlock 保护临界区时,多个读之间无法并发,只能被 s ...

最新文章

  1. R可视化包ggplot2设置轴范围
  2. 禁用outlook2007 垃圾邮件过滤功能
  3. android开发出现No Launcher activity found!解决方案
  4. SpringBoot (14)---日志配置(logback)
  5. using namespace std;的陷阱
  6. silverlight创建新的控件——一个简单的弹出菜单
  7. MySql数据库帮助类:DbHelperMySQL
  8. 对FreeMarker技术的思考
  9. 读《UNIX 编程艺术》
  10. mysql操作 begin 命令_MySQL基础操作
  11. 171021 逆向-Xp0intCTF(re300)
  12. VMware Error | IP地址经常变更
  13. 第二周 计算圆的面积
  14. 厦大计算机推免复试,统考生没戏!厦大该专业3个系招生,推免占比最高100%!...
  15. SAP 使用BDC的一些心得
  16. python 基础一
  17. 验证方法学的发展历程及比较
  18. python创建目录(文件夹)
  19. maya腿的蒙皮旋转枢轴_python Maya烘焙枢轴
  20. 单招计算机网络考什么,单招考什么内容 单招一般考什么科目

热门文章

  1. linux dhclient源码 多进程,重启虚拟机后dhclient进程未运行解决办法
  2. 【软件测试】测试的分类 图片梳理 多图预警
  3. 手把手教你用Python批量实现文件夹下所有Excel文件的第二张表合并
  4. 2023年7月11日
  5. element-ui 时间插件做重置后,无法选择时间和清空的问题
  6. 嵌入式:ARM的DMA设计
  7. dell服务器显示器fre,聊聊几款Dell显示器,大部分是10Bit的。
  8. 6. POD 配置1
  9. 中小型企业如何留住人才,降低其流失率?
  10. 清华 主教计算机培训,【考研 ● 经验】小伙伴来看看!来自清华第一大神的经验...