WiFi驱动(4)SDIO驱动SDIO卡的扫描

1、SDIO卡的扫描流程

接上文:

core.c (drivers\mmc\core)
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
    sdio_reset(host);-->a
    mmc_go_idle(host);-->b

    mmc_send_if_cond(host, host->ocr_avail);-->c
    if (!mmc_attach_sdio(host))-->d
        return 0;
    
    return -EIO;
}

a, 复位卡,见第3小节

b,设置卡状态:idle,见第4小节

c,检测卡支持的工作电压,见第5小节

d,SDIO工作环境设置及card初始化,见第6小节

SDIOb、c步骤是不需要的对于这两个操作,SDIO卡可以不作回应(忽略即可)。


2、SDIO新增命令

为了支持I/O特性,SDIO标准在SD的基础上增加了两条命令
 - IO_RW_DIRECT(CMD52)
 - IO_RW_EXTENDED(CMD53)

2.1 CMD52简介
 - 用来访问单个的寄存器(SD标准中,寄存器空间共128K)
 - 初始化寄存器或为IO功能检测相关状态值
 - 读/写1字节的数据,仅仅需要一个commond/response对(读写单个寄存器最快的方式)

2.2 CMD52命令




2.3 Response R5

注:SD模式下48 bits;SPI模式下16bits




2.4 Command是如何构建的

kernel中命令及Response由结构体mmc_command表示:

struct mmc_command {
	u32			opcode; // Command的操作码
	u32			arg;  // Command携带的参数
	u32			resp[4];  // Command发出后,如果需要应答,结果保存在resp数组中,最多可以保存128bits的应答
	unsigned int		flags;	// 保存该命令所期望的应答类型
	unsigned int		retries;  // 指明最多可重发的次数
	unsigned int		error; // 如果最终还是出错,通过该字段返回错误的原因,例如ETIMEDOUT、EILSEQ、EINVAL、ENOMEDIUM等
	unsigned int		cmd_timeout_ms;	/* in milliseconds */
	bool			sanitize_busy;
	struct mmc_data		*data;		/* data segment associated with cmd */
	struct mmc_request	*mrq;		/* associated request */
};


所以发送Command前就需构建结构体mmc_command。以CMD52为例:

sdio_ops.c (drivers\mmc\core)
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 = {0};

	// 构建Command,#define SD_IO_RW_DIRECT 52 
	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;
	...
	return 0;
}

arg是32bits无符号整形值,而SDIO命令是48bits,对应关系就是cmd[8:39],arg即是

[31] R/W flag
[30:28] Function number
[27] RAW flag
[25:9] Register address
[7:0] Data


2.5 Command是如何发送的

command发送:
mmc_io_rw_direct_host
->mmc_wait_for_cmd
-->mmc_wait_for_req
--->__mmc_start_req
---->mmc_start_request
----->__mmc_start_request
------>host->ops->request(host, mrq)
九曲十八弯后调用struct mmc_host_ops的request
static const struct mmc_host_ops dw_mci_ops = {
	.request		= dw_mci_request,
}

sdio card的response:
-->mmc_wait_for_req_done


3、SDIO卡的复位

SDIO Spec:

In order to reset an I/O only card or the I/O portion of a combo card, use CMD52 to write
a 1 to the RES bit in the CCCR (bit 3 of register 6)

CCCR(Card Common Control Registers):


代码实现:

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);-->a
	if (ret) -->b
		abort = 0x08;
	else
		abort |= 0x08;

	ret = mmc_io_rw_direct_host(host, 1, 0, SDIO_CCCR_ABORT, abort, NULL); -->c
	return ret;
}

a, 读取0x06地址寄存器内容

b, 读取正常(ret=0)只设置RES位,否则写寄存器内容为0x08

c, 写0x06地址寄存器

至此完成card的复位操作。

4、设置卡为IDLE状态

该操作由mmc_go_idle()完成:

int mmc_go_idle(struct mmc_host *host)
{
	if (!mmc_host_is_spi(host)) { -->a
		mmc_set_chip_select(host, MMC_CS_HIGH);
		mmc_delay(1);
	}

	cmd.opcode = MMC_GO_IDLE_STATE; -->b
	cmd.arg = 0;
	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;

	err = mmc_wait_for_cmd(host, &cmd, 0); -->c

	mmc_delay(1);

	if (!mmc_host_is_spi(host)) { -->d
		mmc_set_chip_select(host, MMC_CS_DONTCARE);
		mmc_delay(1);
	}

	host->use_spi_crc = 0;

	return err;
}
a, 在SD模式下, 把片选pin拉高避免进入SPI模式。看下图就一目了然:

b, 构建CMD0,SDIO对此命令不做反应!

c, 发送CMD0

d, SD模式下不关心片选引脚状态(作为数据DAT3存在)


5、检测卡支持的工作电压

通过命令CMD8检测card支持的工作电压。

int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
{
	struct mmc_command cmd = {0};
	int err;
	static const u8 test_pattern = 0xAA;
	u8 result_pattern;

	cmd.opcode = SD_SEND_IF_COND;
	cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; // cmd.arg = 0x1AA
	cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;

	err = mmc_wait_for_cmd(host, &cmd, 0);

	if (mmc_host_is_spi(host))
		result_pattern = cmd.resp[1] & 0xFF;
	else
		result_pattern = cmd.resp[0] & 0xFF;

	if (result_pattern != test_pattern)
		return -EIO;

	return 0;
}
参照CMD8命令的说明,mmc_send_if_cond就很好理解了:

cmd8:


voltage supplied:

When the card is in idle state, the host shall issue CMD8 before ACMD41. In the argument, 'voltage supplied' is set to the host supply voltage and 'check pattern' is set to any 8-bit pattern.


The card checks whether it can operate on the host's supply voltage
. The card that accepted the supplied voltage returnsR7 response.In the response , the card echoes back both the voltage range and check pattern set in the argument. If the carddoes not support the host supply voltage, it shall not return response andstays in Idle state.
It is recommeded to use '10101010b'(0xAA) for the 'check pattern'.


6、SDIO工作环境设置

6.1 查询card支持的操作电压

int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
	cmd.opcode = SD_IO_SEND_OP_COND; ----------->a
	cmd.arg = ocr;
	cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;

	{
		err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);------>b

		/* if we're just probing, do a single pass */
		if (ocr == 0)
			break;

		/* otherwise wait until reset completes */
		if (mmc_host_is_spi(host)) {
		} else {
			if (cmd.resp[0] & MMC_CARD_BUSY)------>c
				break;
		}
	}

	if (rocr)
		*rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0];

	return err;
}
a, 通过 IO_SEND_OP_COND(CMD5)命令查询card的支持电压范围。



b, 发送命令。Response保存在R4中:


c, resp[31:0] = R4[39:8]。

bit C: 1:card ready。这里check card是否ready。


6.2 确定host、card都支持的最低电压

u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{
	int bit;

	if (ocr & 0x7F) { --------------------->a
		dev_warn(mmc_dev(host),
			 "card claims to support voltages below defined range\n");
		ocr &= ~0x7F;
	}

	ocr &= host->ocr_avail;----->b
	if (!ocr) {
		dev_warn(mmc_dev(host), "no support for card's volts\n");
		return 0;
	}

	if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
		bit = ffs(ocr) - 1;
		ocr &= 3 << bit;
		mmc_power_cycle(host, ocr);
	} else { -------------------->c
		bit = fls(ocr) - 1;
		ocr &= 3 << bit;
		if (bit != host->ios.vdd) ---->d
			dev_warn(mmc_dev(host), "exceeding card's volts\n");
	}

	return ocr;
}

a, CMD5的OCD为24 bits,其中bit[7:0]为Reserved,所以正常情况下其值为0。这里检测避免出现不支持的数据。

b, 参数ocr从card读取,ocr_avail是host支持的电压范围,所以这里用来确定card、host双方都支持的电压

c, fls(x):返回x中第一个非0bit的位置,返回值[1:32],ffs(1)=1,ffs(8)=4,特别的,fls(0) = 0。所以这里作用就是返回双方都支持的最低电压

d, bit = fls(ocr) - 1而host->ios.vdd = fls(ocr) - 1,所以两者应该相同,否则就是上面说的 "exceeding card's volts"。

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值