imx6 android4.4.2内核sd卡platform_device注册加载原理

单板环境:
linux内核:3.0.8
安卓:4.4.2

作者:254008829@qq.com


1.概述

此版飞思卡尔imx6SDK的内核是3.0.8还没有引入设备树,所以还是传统的platform_deviceplatform_driver方式进行注册和驱动加载的。

2.platform_device的 注册入口点

arch/arm/mach-mx6/board-mx6q_sabresd.c文件中函数mx6_sabresd_board_init调用imx6q_add_sdhci_usdhc_imx现实的。关系如下:

board-mx6q_sabresd.c
	mx6_sabresd_board_in
		imx6q_add_sdhci_usdhc_imx

imx6q_add_sdhci_usdhc_imx是一个宏函数,它有两个参数,@arg1:id@arg2:pdata

id表示sdio的通道号,如我们单板SDIO1(从1开始数)外接的是SD卡,其对应的id等于0

pdataconststruct esdhc_platform_data类型的指针。

3.struct esdhc_platform_data结构详解

struct esdhc_platform_data {
	unsigned int wp_gpio;
	unsigned int cd_gpio;
	enum cd_types cd_type;
	unsigned int always_present;
	unsigned int support_18v;
	unsigned int support_8bit;
	unsigned int keep_power_at_suspend;
	unsigned int delay_line;
	bool runtime_pm;
	int (*platform_pad_change)(unsigned int index, int clock);
};

wp_gpio:如果外设芯片有写保护引脚接到IMXCPU,则将它的值赋值给它,否则填-EINVALimx6引脚值都统一用宏IMX_GPIO_NR来定义,如GPIO_5_7,则其值为IMX_GPIO_NR(5,7)

cd_gpio:如果外设是有中断检测引脚来检测外设是否插入,则需要给此成员赋值。典型的应用是SD卡,一般都是需要支持热插拔的,有中断检测引脚。将引脚值用IMX_GPIO_NR来定义。

cd_type

	enum cd_types {
	ESDHC_CD_NONE,          /* no CD, neither controller nor gpio */
	ESDHC_CD_CONTROLLER,    /* mmc controller internal CD */
	ESDHC_CD_GPIO,          /* external gpio pin for CD */
	ESDHC_CD_PERMANENT,     /* no CD, card permanently wired to host */
};

如果cd_gpio定义了,则cd_typeESDHC_CD_GPIO

always_present:是否一直在卡槽里,或者一直和IMXCPUSDIO连接,如果是则填1,否则为0

support_8bit:这是一个关键的参数。一般SD卡是4bit数据线,EMMC8bit数据线,如果是8bit则为1,否则为0。我的外设是SD卡所以此值为0


board-mx6q_sabresd.c中定义一个自己的conststruct esdhc_platform_data变量,如下:

// sdcard
static const struct esdhc_platform_data mx6q_sabresd_sd1_data __initconst = {
    .cd_gpio = T6_V2_SD_DET,
    .wp_gpio = -EINVAL,
	.keep_power_at_suspend = 1,
	.support_8bit = 0,
	.delay_line = 0,
    .cd_type = ESDHC_CD_GPIO,
    .runtime_pm = 1,
};

调用函数将之注册:imx6q_add_sdhci_usdhc_imx(0,&mx6q_sabresd_sd1_data);

4.深度解析imx6q_add_sdhci_usdhc_imx

此函数是一个宏函数,定义如下:

#defineimx6q_add_sdhci_usdhc_imx(id, pdata) \

imx_add_sdhci_esdhc_imx(&imx6q_sdhci_usdhc_imx_data[id],pdata)

接着会调用imx_add_sdhci_esdhc_imx,其中第一个参数变成了一个数组的第id个元素的指针。

关键点在于imx6q_sdhci_usdhc_imx_data是干什么的?

往后追代码:

const struct imx_sdhci_esdhc_imx_data imx6q_sdhci_usdhc_imx_data[] = {
#define imx6q_sdhci_usdhc_imx_data_entry(_id, _hwid)			\
	imx_sdhci_usdhc_imx_data_entry(MX6Q, _id, _hwid)
	imx6q_sdhci_usdhc_imx_data_entry(0, 1),
	imx6q_sdhci_usdhc_imx_data_entry(1, 2),
	imx6q_sdhci_usdhc_imx_data_entry(2, 3),
	imx6q_sdhci_usdhc_imx_data_entry(3, 4),
};

结构数据中对应结构的定义为:

struct imx_sdhci_esdhc_imx_data {
	int id;
	resource_size_t iobase;
	resource_size_t irq;
};

imx_sdhci_usdhc_imx_data_entry这个宏从字面上来看就是imxsd数据项,这种和具体平台相关的数据有哪些呢,无非是控制器的iobaseirq的编号等等。层层解剖马上就见真面目了。

#define imx_sdhci_esdhc_imx_data_entry(soc, id, hwid)	\
	[id] = imx_sdhci_esdhc_imx_data_entry_single(soc, id, hwid)

#define imx_sdhci_esdhc_imx_data_entry_single(soc, _id, hwid) \
	{								\
		.id = _id,						\
		.iobase = soc ## _ESDHC ## hwid ## _BASE_ADDR,	\
		.irq = soc ## _INT_ESDHC ## hwid,			\
	}

至此总结下:imx6q_add_sdhci_usdhc_imx函数通过idpdata。将一个和imx6平台相关的structimx_sdhci_esdhc_imx_data的结构指针和mx6q_sabresd_sd1_data&mx6q_sabresd_sd1_data就是下文多次提到的pdata)的地址传给了调用者imx_add_sdhci_esdhc_imx

5.imx_add_sdhci_esdhc_imx函数什么的干活

定义如下:

struct platform_device *__init imx_add_sdhci_esdhc_imx(
		const struct imx_sdhci_esdhc_imx_data *data,
		const struct esdhc_platform_data *pdata)
{
	struct resource res[] = {
		{
			.start = data->iobase,
			.end = data->iobase + SZ_16K - 1,
			.flags = IORESOURCE_MEM,
		}, {
			.start = data->irq,
			.end = data->irq,
			.flags = IORESOURCE_IRQ,
		},
	};

	return imx_add_platform_device_dmamask("sdhci-esdhc-imx", data->id, res,
			ARRAY_SIZE(res), pdata, sizeof(*pdata), DMA_BIT_MASK(32));
}

此函数将iobaseirq等硬件资源生成一个linux标准的structresource res对象。

终极调用者为imx_add_platform_device_dmamask,此函数指定了platform_device的名字,id,资源对象,以及在board-mx6q_sabresd.c中定义的一个pdata&mx6q_sabresd_sd1_data)。最后一个参数DMA_BIT_MASK(32)是设定DMA的工作位数。

imx_add_platform_device_dmamask函数定义:

struct platform_device *__init imx_add_platform_device_dmamask(
		const char *name, int id,
		const struct resource *res, unsigned int num_resources,
		const void *data, size_t size_data, u64 dmamask)
{
	int ret = -ENOMEM;
	struct platform_device *pdev;

	pdev = platform_device_alloc(name, id);
	if (!pdev)
		goto err;

	if (dmamask) {
		/*
		 * This memory isn't freed when the device is put,
		 * I don't have a nice idea for that though.  Conceptually
		 * dma_mask in struct device should not be a pointer.
		 * See http://thread.gmane.org/gmane.linux.kernel.pci/9081
		 */
		pdev->dev.dma_mask =
			kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
		if (!pdev->dev.dma_mask)
			/* ret is still -ENOMEM; */
			goto err;

		*pdev->dev.dma_mask = dmamask;
		pdev->dev.coherent_dma_mask = dmamask;
	}

	if (res) {
		ret = platform_device_add_resources(pdev, res, num_resources);
		if (ret)
			goto err;
	}

	if (data) {
		ret = platform_device_add_data(pdev, data, size_data);
		if (ret)
			goto err;
	}

	ret = platform_device_add(pdev);
	if (ret) {
err:
		if (dmamask)
			kfree(pdev->dev.dma_mask);
		platform_device_put(pdev);
		return ERR_PTR(ret);
	}

	return pdev;
}

这函数比较容易,定义一个structplatform_device类型的指针pdev。将传入的respdata添加进入pdev成员里。最后调用platform_device_add(pdev)将之注册进linux系统。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值