ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux mmc驱动框架(3)——host驱动初始化

2021-11-03 11:34:06  阅读:234  来源: 互联网

标签:detect struct probe host mmc Linux 驱动 sunxi


Linux mmc驱动框架


host驱动的编写主要步骤如下:

  1. 通过mmc_alloc_host分配一个mmc_host结构体
  2. 定义实现mmc_host_ops数据结构, 并赋值给上面的mmc_host->mmc_host_ops成员变量
  3. 给mmc_host成员变量赋值, 如ocr_avail、caps等成员变量
  4. 调用mmc_add_host注册该Host

Host驱动设备树

  host驱动设备树用于匹配host驱动,host驱动匹配上设备树,初始化流程才能开始。本文举例全志H3设备树以及mmc控制器驱动。
  下面的代码段都是一些基本配置,compatible用于与驱动匹配,reg为控制器IO内存地址。interrupt为中断参数,GIC控制器,中断号等。以及mmc控制器用到的时钟参数。

mmc0: mmc@01c0f000 {
	/* compatible and clocks are in per SoC .dtsi file */
	compatible = "allwinner,sun7i-a20-mmc";
	reg = <0x01c0f000 0x1000>;
	resets = <&ccu RST_BUS_MMC0>;
	reset-names = "ahb";
	interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
	status = "disabled";
	#address-cells = <1>;
	#size-cells = <0>;
	clocks = <&ccu CLK_BUS_MMC0>,
		 <&ccu CLK_MMC0>,
		 <&ccu CLK_MMC0_OUTPUT>,
		 <&ccu CLK_MMC0_SAMPLE>;
	clock-names = "ahb",
		      "mmc",
		      "output",
		      "sample";
};

   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Host驱动

platform_driver数据结构

static struct platform_driver sunxi_mmc_driver = {
	.driver = {
		.name	= "sunxi-mmc",
		.of_match_table = of_match_ptr(sunxi_mmc_of_match),
		.pm = &sunxi_mmc_pm_ops,
	},
	.probe		= sunxi_mmc_probe,
	.remove		= sunxi_mmc_remove,
};

static const struct of_device_id sunxi_mmc_of_match[] = {
{ .compatible = “allwinner,sun4i-a10-mmc”, .data = &sun4i_a10_cfg },
{ .compatible = “allwinner,sun5i-a13-mmc”, .data = &sun5i_a13_cfg },
{ .compatible = “allwinner,sun7i-a20-mmc”, .data = &sun7i_a20_cfg }, (1)
{ .compatible = “allwinner,sun8i-a83t-emmc”, .data = &sun8i_a83t_emmc_cfg },
{ .compatible = “allwinner,sun9i-a80-mmc”, .data = &sun9i_a80_cfg },
{ .compatible = “allwinner,sun50i-a64-mmc”, .data = &sun50i_a64_cfg },
{ .compatible = “allwinner,sun50i-a64-emmc”, .data = &sun50i_a64_emmc_cfg },
{ /* sentinel */ }
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

(1)这里的compatible和设备树中对应,内核初始化时驱动匹配上设备树,会调用platform_driver 的probe函数。

控制器驱动初始化函数

  首先来看控制器驱动的probe函数:

static int sunxi_mmc_probe(struct platform_device *pdev)
{
	struct sunxi_mmc_host *host;
	struct mmc_host *mmc;
	int ret;
mmc <span class="token operator">=</span> <span class="token function">mmc_alloc_host</span><span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">struct</span> sunxi_mmc_host<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>pdev<span class="token operator">-&gt;</span>dev<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mmc<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token function">dev_err</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>pdev<span class="token operator">-&gt;</span>dev<span class="token punctuation">,</span> <span class="token string">"mmc alloc host failed\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">return</span> <span class="token operator">-</span>ENOMEM<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
mmc<span class="token operator">-&gt;</span>ops		<span class="token operator">=</span> <span class="token operator">&amp;</span>sunxi_mmc_ops<span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
ret <span class="token operator">=</span> <span class="token function">sunxi_mmc_init_host</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>ret<span class="token punctuation">)</span>
	<span class="token keyword">goto</span> error_free_dma<span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
ret <span class="token operator">=</span> <span class="token function">mmc_add_host</span><span class="token punctuation">(</span>mmc<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token keyword">return</span> ret<span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

  probe函数中首先会申请mmc_host数据结构,调用mmc_alloc_host函数申请数据结构内存,并设置部分mmc_host数据结构参数。
  随后设置struct mmc_hoststruct mmc_host_ops数据结构。下面来看下sunxi_mmc_ops数据结构:

static const struct mmc_host_ops sunxi_mmc_ops = {
	.request	 = sunxi_mmc_request,
	.set_ios	 = sunxi_mmc_set_ios,
	.get_ro		 = mmc_gpio_get_ro,
	.get_cd		 = mmc_gpio_get_cd,
	.enable_sdio_irq = sunxi_mmc_enable_sdio_irq,
	.start_signal_voltage_switch = sunxi_mmc_volt_switch,
	.hw_reset	 = sunxi_mmc_hw_reset,
	.card_busy	 = sunxi_mmc_card_busy,
};

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  struct mmc_host_ops数据结构用来描述卡控制器操作接口函数功能,用于从主机控制器层向 core 层注册操作函数,从而将core 层与具体的主机控制器隔离。
  core 要操作主机控制器,就用这个 ops 当中给的函数指针操作,不能直接调用具体主控制器的函数,从而实现逻辑层和硬件驱动层的分离。

  再继续看sunxi_mmc_probe函数,ret = sunxi_mmc_init_host(host);这部分代码,主要是涉及到硬件相关的mmc控制器初始化代码,这里就不展开了。
  最后调用mmc_add_host,顾名思义,添加一个 mmc_host 。下面来看一下mmc_add_host代码。

sunxi_mmc_probe->mmc_add_host

int mmc_add_host(struct mmc_host *host)
{
	int err;
<span class="token function">WARN_ON</span><span class="token punctuation">(</span><span class="token punctuation">(</span>host<span class="token operator">-&gt;</span>caps <span class="token operator">&amp;</span> MMC_CAP_SDIO_IRQ<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span>
	<span class="token operator">!</span>host<span class="token operator">-&gt;</span>ops<span class="token operator">-&gt;</span>enable_sdio_irq<span class="token punctuation">)</span><span class="token punctuation">;</span>

err <span class="token operator">=</span> <span class="token function">device_add</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>host<span class="token operator">-&gt;</span>class_dev<span class="token punctuation">)</span><span class="token punctuation">;</span>						<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span>
	<span class="token keyword">return</span> err<span class="token punctuation">;</span>

<span class="token function">led_trigger_register_simple</span><span class="token punctuation">(</span><span class="token function">dev_name</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>host<span class="token operator">-&gt;</span>class_dev<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>host<span class="token operator">-&gt;</span>led<span class="token punctuation">)</span><span class="token punctuation">;</span>

#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
#endif

<span class="token function">mmc_start_host</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span>									<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span>
<span class="token function">mmc_register_pm_notifier</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

  很简单,就是增加了一个 device ,然后就调用 mmc_start_host 了,下面来看 mmc_start_host

sunxi_mmc_probe->mmc_add_host->mmc_start_host

void mmc_start_host(struct mmc_host *host)
{
	host->f_init = max(freqs[0], host->f_min);
	host->rescan_disable = 0;
	host->ios.power_mode = MMC_POWER_UNDEFINED;
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>host<span class="token operator">-&gt;</span>caps2 <span class="token operator">&amp;</span> MMC_CAP2_NO_PRESCAN_POWERUP<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
	<span class="token function">mmc_claim_host</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">mmc_power_up</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> host<span class="token operator">-&gt;</span>ocr_avail<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token function">mmc_release_host</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">mmc_gpiod_request_cd_irq</span><span class="token punctuation">(</span>host<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">_mmc_detect_change</span><span class="token punctuation">(</span>host<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> false<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

sunxi_mmc_probe->mmc_add_host->mmc_start_host-> _mmc_detect_change

static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
				bool cd_irq)
{
	if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) &&
		device_can_wakeup(mmc_dev(host)))
		pm_wakeup_event(mmc_dev(host), 5000);
host<span class="token operator">-&gt;</span>detect_change <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token function">mmc_schedule_delayed_work</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>host<span class="token operator">-&gt;</span>detect<span class="token punctuation">,</span> delay<span class="token punctuation">)</span><span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

sunxi_mmc_probe->mmc_add_host->mmc_start_host-> _mmc_detect_change-> mmc_schedule_delayed_work

static int mmc_schedule_delayed_work(struct delayed_work *work,
				     unsigned long delay)
{
	return queue_delayed_work(system_freezable_wq, work, delay);
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5

  mmc_start_host代码很简单,主要代码为调用了_mmc_detect_change函数。
  _mmc_detect_change函数调用mmc_schedule_delayed_work
  mmc_schedule_delayed_work函数最终调用了queue_delayed_work
  queue_delayed_work具体是干嘛的可以百度,这里
  这几个代码告诉我们在 workqueue 这个工作队列当中添加一个延迟的工作任务,而这个工作任务就是由 host->detect 来描述的,在随后的 delay 个 jiffies 后会有一个记录在 host->detect 里面的函数被执行,上面可以看到_mmc_detect_change传递的delay参数为0,意思是不延迟?大概是为了预留需要延迟检测卡的情况。
  代码执行到这里 sunxi_mmc_probe这个函数基本上执行完成了,host(控制器)的初始化完成了。
  但事情还没有完, workqueue 这个工作队列还在忙,不一会儿它就会调用 host->detect 里面那个函数,这个函数到底是哪个函数,到底是用来干什么的呢?好像没有看到,detect 包含在 host 里面,那估计是在刚才那个申请的地方设置的那个函数,回过头来看一下 mmc_alloc_host

sunxi_mmc_probe->mmc_alloc_host

struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
	struct mmc_host *host;
host <span class="token operator">=</span> <span class="token function">kzalloc</span><span class="token punctuation">(</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">struct</span> mmc_host<span class="token punctuation">)</span> <span class="token operator">+</span> extra<span class="token punctuation">,</span> GFP_KERNEL<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>host<span class="token punctuation">)</span>
	<span class="token keyword">return</span> <span class="token constant">NULL</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token function">INIT_DELAYED_WORK</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>host<span class="token operator">-&gt;</span>detect<span class="token punctuation">,</span> mmc_rescan<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token keyword">return</span> host<span class="token punctuation">;</span>

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  INIT_DELAYED_WORK初始化延迟工作队列,延时结束后调用工作队列函数,这里是mmc_rescan,这个函数是用来检测卡的。

标签:detect,struct,probe,host,mmc,Linux,驱动,sunxi
来源: https://blog.csdn.net/u014286440/article/details/121117545

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有