Linux驱动——sdio type card(八)
生活随笔
收集整理的這篇文章主要介紹了
Linux驱动——sdio type card(八)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
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
- 創(chuàng)建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相關代碼:
數據結構
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發(fā)送參數為0的ACMD41命令,提取response中的VHS,// 得到card支持的工作電壓范圍err = mmc_send_io_op_cond(host, 0, &ocr);if (err)return err;//創(chuàng)建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的工作電壓設置為這個值。// 后續(xù)就以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選擇了一個合適的工作電壓 **//** 后續(xù)調用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的配置寄存器和狀態(tài)寄存器 **//** 讀取card 的switch狀態(tài),也就是其支持的function **/// host發(fā)送ACMD51命令,要求card回復其SCR寄存器(SD configuration register)的值// host發(fā)送ACMD13命令,要求card回復其SSR寄存器(SD status regiter)的值// host發(fā)送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);//發(fā)送數據,并且等待返回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, ®);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;// 創(chuàng)建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, ®);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);創(chuàng)建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);//創(chuàng)建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,優(yōu)先級為1sched_setscheduler(current, SCHED_FIFO, ¶m);/** 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;}// 發(fā)送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相關的函數,最終都是調用本函數來實現相關的功能,具體實現方式如下:
總結
以上是生活随笔為你收集整理的Linux驱动——sdio type card(八)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 项目管理杂谈:工期估算
- 下一篇: 单细胞转录组基础知识详解