Linux驱动——mmc host浅析(四)

Linux驱动——mmc host浅析(四)

备注:
  1. Kernel版本:5.4
  2. 使用工具:Source Insight 4.0
  3. 参考博客:
4. [mmc subsystem] mmc core(第四章)——host模块说明

概述

  对应代码drivers/mmc/core/host.c,drivers/mmc/core/host.h。为底层host controller driver实现mmc host的申请以及注册的API等等,以及host相关属性的实现。

API预览

mmc host分配、注册相关

  • mmc_alloc_host & mmc_free_host

  底层host controller驱动调用,用来分配或者释放一个struct mmc_host结构体,将其于mmc_host_class关联,并且做部分初始化操作。

// 参数说明:
// extra——>mmc_host的私有数据的长度,
//         会和mmc_host结构体一起分配,
//   dev——>底层host controller的device结构体,
//         用于作为mmc_host的device的父设备
struct mmc_host *mmc_alloc_host(int extra, struct device *dev);

void mmc_free_host(struct mmc_host *host);
  • mmc_add_host & mmc_remove_host

  底层host controller驱动调用,注册或者卸载mmc_host到设备驱动中,添加到sys类下面,并设置相应的debug目录。然后启动mmc_host。

int mmc_add_host(struct mmc_host *host);
void mmc_remove_host(struct mmc_host *host);

mmc host class相关

  • mmc_register_host_class & mmc_unregister_host_class
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);

mmc host属性解析相关

  • mmc_of_parse

  底层host controller驱动调用,解析mmc_host的dtsi节点的部分属性。

void mmc_of_parse(struct mmc_host *host);

核心接口函数实现

mmc_register_host_class实现

注册mmc_host class。

int mmc_register_host_class(void)
{
    // 注册mmc_host类
    return class_register(&mmc_host_class);   
}
void mmc_unregister_host_class(void)
{
	// 卸载mmc_host类
	class_unregister(&mmc_host_class);
}

mmc_alloc_host实现

  底层host controller驱动调用,用来分配一个struct mmc_host结构体,将其于mmc_host_class关联,并且做部分初始化操作。

  • 主要工作:
    分配内存空间初始化其class device(对应/sys/class/mmc0节点)
    初始化detect成员(也就是检测工作)为mmc_rescan
    初始化 max_segs、max_req_size等
/**
 *	mmc_alloc_host - initialise the per-host structure.
 *	@extra: sizeof private data structure
 *	@dev: pointer to host device model structure
 *
 *	Initialise the per-host structure.
 */
// 参数说明:
// extra——>mmc_host的私有数据的长度,
//         会和mmc_host结构体一起分配,
//   dev——>底层host controller的device结构体,
//         用于作为mmc_host的device的父设备
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
	int err;
	struct mmc_host *host;
	// 申请mmc_host内存,预申请私有数据的内存
	host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
	if (!host)
		return NULL;

	/* scanning will be enabled when we're ready */
	/* 
	 * 因为只是分配了一个mmc_host,host还没有准备好,
	 * 所以这里禁用rescan,也就是设置mmc_host->rescan_disable 
	 */
	host->rescan_disable = 1; // 在在mmc_start_host中会去使能

	// 为该mmc_host分配一个唯一的id号,设置到host->index
	err = ida_simple_get(&mmc_host_ida, 0, 0, GFP_KERNEL);
	if (err < 0) {
		kfree(host);
		return NULL;
	}

	host->index = err;
	/* 设置mmc_host name */
	// 以mmc_host的id号构成mmc_host的name,例如mmc0、mmc1
	dev_set_name(&host->class_dev, "mmc%d", host->index);

	/* 关联mmc_host class_dev并进行初始化 */
	/* class_dev就代表了mmc_host 的device结构体,是其在设备驱动模型中的体现 */
	// 将mmc_host的parent设置成对应host controller节点转化出来的device
	host->parent = dev;
	host->class_dev.parent = dev;
	host->class_dev.class = &mmc_host_class;
	device_initialize(&host->class_dev); // 初始化mmc_host->class_dev
	device_enable_async_suspend(&host->class_dev);

	// 申请cd-gpio、ro-gpio
	if (mmc_gpio_alloc(host)) {
		put_device(&host->class_dev);
		return NULL;
	}

	spin_lock_init(&host->lock);
	init_waitqueue_head(&host->wq);

	// 初始化detect工作为mmc_rescan,
	// 后续调度host->detect来检测是否有card插入时,
	// 就会调用到mmc_rescan
	INIT_DELAYED_WORK(&host->detect, mmc_rescan);

	// 创建 irq work task
	INIT_DELAYED_WORK(&host->sdio_irq_work, sdio_irq_work);
	timer_setup(&host->retune_timer, mmc_retune_timer, 0);

	/*
	 * By default, hosts do not support SGIO or large requests.
	 * They have to set these according to their abilities.
	 */
	/* 初始化 host 基本参数 */
	host->max_segs = 1;
	host->max_seg_size = PAGE_SIZE;

	host->max_req_size = PAGE_SIZE;
	host->max_blk_size = 512;
	host->max_blk_count = PAGE_SIZE / 512;

	host->fixed_drv_type = -EINVAL;
	host->ios.power_delay_ms = 10;

	return host;
}

mmc_add_host实现

  底层host controller驱动调用,注册mmc_host到设备驱动中,添加到sys类下面,并设置相应的debug目录。然后启动mmc_host。

  • 主要工作:
    将mmc_host的class_dev添加到设备驱动模型中,在sysfs中生成相应的节点
    初始化mmc_host相关的debug目录
    设置mmc_host的class_dev的属性
    调用mmc_start_host启动host
/**
 *	mmc_add_host - initialise host hardware
 *	@host: mmc host
 *
 *	Register the host with the driver model. The host must be
 *	prepared to start servicing requests before this function
 *	completes.
 */
int mmc_add_host(struct mmc_host *host)
{
	int err;

	// 检查 MMC_CAP_SDIO_IRQ 属性
	WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
		!host->ops->enable_sdio_irq);

	/* 
	 * 通过device_add将mmc_host->class_dev添加到设备驱动模型中,
	 * 在sys下生成相应节点 
	 */
	err = device_add(&host->class_dev);
	if (err)
		return err;

	led_trigger_register_simple(dev_name(&host->class_dev), &host->led);

#ifdef CONFIG_DEBUG_FS
	/* 设置mmc_host的debugfs节点 */
	mmc_add_host_debugfs(host);
#endif

	mmc_start_host(host);//启动host,卡识别等
	mmc_register_pm_notifier(host);

	return 0;
}

mmc_of_parse实现

  解析mmc_host的dtsi节点的部分属性。
  mmc_of_parse提供了通用的、解析host controller dtsi节点的属性的方法,这就要依赖于dtsi的属性是否符合规范。
  但是host controller driver并一定要使用这个,也可以使用自己一套解析的方法。

/**
 *	mmc_of_parse() - parse host's device-tree node
 *	@host: host whose node should be parsed.
 *
 * To keep the rest of the MMC subsystem unaware of whether DT has been
 * used to to instantiate and configure this host instance or not, we
 * parse the properties and set respective generic mmc-host flags and
 * parameters.
 */
int mmc_of_parse(struct mmc_host *host)
{
	struct device *dev = host->parent;
	u32 bus_width, drv_type, cd_debounce_delay_ms;
	int ret;
	bool cd_cap_invert, cd_gpio_invert = false;

	if (!dev || !dev_fwnode(dev))
		return 0;

	/* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */
	// 获取数据线宽度
	if (device_property_read_u32(dev, "bus-width", &bus_width) < 0) {
		dev_dbg(host->parent,
			"\"bus-width\" property is missing, assuming 1 bit.\n");
		bus_width = 1;
	}

	switch (bus_width) {
	case 8:
		host->caps |= MMC_CAP_8_BIT_DATA;
		/* fall through - Hosts capable of 8-bit can also do 4 bits */
	case 4:
		host->caps |= MMC_CAP_4_BIT_DATA;
		break;
	case 1:
		break;
	default:
		dev_err(host->parent,
			"Invalid \"bus-width\" value %u!\n", bus_width);
		return -EINVAL;
	}

	/* f_max is obtained from the optional "max-frequency" property */
	// 获取最大频率
	device_property_read_u32(dev, "max-frequency", &host->f_max);

	/*
	 * Configure CD and WP pins. They are both by default active low to
	 * match the SDHCI spec. If GPIOs are provided for CD and / or WP, the
	 * mmc-gpio helpers are used to attach, configure and use them. If
	 * polarity inversion is specified in DT, one of MMC_CAP2_CD_ACTIVE_HIGH
	 * and MMC_CAP2_RO_ACTIVE_HIGH capability-2 flags is set. If the
	 * "broken-cd" property is provided, the MMC_CAP_NEEDS_POLL capability
	 * is set. If the "non-removable" property is found, the
	 * MMC_CAP_NONREMOVABLE capability is set and no card-detection
	 * configuration is performed.
	 */

	/* Parse Card Detection */
	// 获取卡不可移除标志
	if (device_property_read_bool(dev, "non-removable")) {
		host->caps |= MMC_CAP_NONREMOVABLE;
	} else {
		cd_cap_invert = device_property_read_bool(dev, "cd-inverted");

		if (device_property_read_u32(dev, "cd-debounce-delay-ms",
					     &cd_debounce_delay_ms))
			cd_debounce_delay_ms = 200;

		// 获取轮询查询卡状态标志
		if (device_property_read_bool(dev, "broken-cd"))
			host->caps |= MMC_CAP_NEEDS_POLL;

		// 申请cd-gpio管脚
		ret = mmc_gpiod_request_cd(host, "cd", 0, false,
					   cd_debounce_delay_ms * 1000,
					   &cd_gpio_invert);
		if (!ret)
			dev_info(host->parent, "Got CD GPIO\n");
		else if (ret != -ENOENT && ret != -ENOSYS)
			return ret;

		/*
		 * There are two ways to flag that the CD line is inverted:
		 * through the cd-inverted flag and by the GPIO line itself
		 * being inverted from the GPIO subsystem. This is a leftover
		 * from the times when the GPIO subsystem did not make it
		 * possible to flag a line as inverted.
		 *
		 * If the capability on the host AND the GPIO line are
		 * both inverted, the end result is that the CD line is
		 * not inverted.
		 */
		if (cd_cap_invert ^ cd_gpio_invert)
			host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
	}

	/* Parse Write Protection */
	// 获取写保护标志
	if (device_property_read_bool(dev, "wp-inverted"))
		host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;

	ret = mmc_gpiod_request_ro(host, "wp", 0, 0, NULL);
	if (!ret)
		dev_info(host->parent, "Got WP GPIO\n");
	else if (ret != -ENOENT && ret != -ENOSYS)
		return ret;

	if (device_property_read_bool(dev, "disable-wp"))
		host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;

	// 获取sd highspeed标志
	if (device_property_read_bool(dev, "cap-sd-highspeed"))
		host->caps |= MMC_CAP_SD_HIGHSPEED;

	// 获取mmc highspeed标志
	if (device_property_read_bool(dev, "cap-mmc-highspeed"))
		host->caps |= MMC_CAP_MMC_HIGHSPEED;
	if (device_property_read_bool(dev, "sd-uhs-sdr12"))
		host->caps |= MMC_CAP_UHS_SDR12;
	if (device_property_read_bool(dev, "sd-uhs-sdr25"))
		host->caps |= MMC_CAP_UHS_SDR25;
	if (device_property_read_bool(dev, "sd-uhs-sdr50"))
		host->caps |= MMC_CAP_UHS_SDR50;
	if (device_property_read_bool(dev, "sd-uhs-sdr104"))
		host->caps |= MMC_CAP_UHS_SDR104;
	if (device_property_read_bool(dev, "sd-uhs-ddr50"))
		host->caps |= MMC_CAP_UHS_DDR50;

	// 获取card 可掉电标志
	if (device_property_read_bool(dev, "cap-power-off-card"))
		host->caps |= MMC_CAP_POWER_OFF_CARD;

	// 获取card 硬件复位标志
	if (device_property_read_bool(dev, "cap-mmc-hw-reset"))
		host->caps |= MMC_CAP_HW_RESET;

	// 获取sdio hw irq标志
	if (device_property_read_bool(dev, "cap-sdio-irq"))
		host->caps |= MMC_CAP_SDIO_IRQ;
	if (device_property_read_bool(dev, "full-pwr-cycle"))
		host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;

	// 在suspend模式下,应保持上电
	if (device_property_read_bool(dev, "keep-power-in-suspend"))
		host->pm_caps |= MMC_PM_KEEP_POWER;

	// 获取唤醒源标志
	if (device_property_read_bool(dev, "wakeup-source") ||
	    device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */
		host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
	if (device_property_read_bool(dev, "mmc-ddr-3_3v"))
		host->caps |= MMC_CAP_3_3V_DDR;
	if (device_property_read_bool(dev, "mmc-ddr-1_8v"))
		host->caps |= MMC_CAP_1_8V_DDR;
	if (device_property_read_bool(dev, "mmc-ddr-1_2v"))
		host->caps |= MMC_CAP_1_2V_DDR;
	if (device_property_read_bool(dev, "mmc-hs200-1_8v"))
		host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
	if (device_property_read_bool(dev, "mmc-hs200-1_2v"))
		host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
	if (device_property_read_bool(dev, "mmc-hs400-1_8v"))
		host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
	if (device_property_read_bool(dev, "mmc-hs400-1_2v"))
		host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
	if (device_property_read_bool(dev, "mmc-hs400-enhanced-strobe"))
		host->caps2 |= MMC_CAP2_HS400_ES;

	// 获取 host不支持的card标志
	if (device_property_read_bool(dev, "no-sdio"))
		host->caps2 |= MMC_CAP2_NO_SDIO;
	if (device_property_read_bool(dev, "no-sd"))
		host->caps2 |= MMC_CAP2_NO_SD;
	if (device_property_read_bool(dev, "no-mmc"))
		host->caps2 |= MMC_CAP2_NO_MMC;

	/* Must be after "non-removable" check */
	if (device_property_read_u32(dev, "fixed-emmc-driver-type", &drv_type) == 0) {
		if (host->caps & MMC_CAP_NONREMOVABLE)
			host->fixed_drv_type = drv_type;
		else
			dev_err(host->parent,
				"can't use fixed driver type, media is removable\n");
	}

	host->dsr_req = !device_property_read_u32(dev, "dsr", &host->dsr);
	if (host->dsr_req && (host->dsr & ~0xffff)) {
		dev_err(host->parent,
			"device tree specified broken value for DSR: 0x%x, ignoring\n",
			host->dsr);
		host->dsr_req = 0;
	}

	device_property_read_u32(dev, "post-power-on-delay-ms",
				 &host->ios.power_delay_ms);

	return mmc_pwrseq_alloc(host);
}
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值