您的位置:首页 > 移动开发 > Android开发

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

2016-04-08 17:58 489 查看
单板环境:

linux内核:3.0.8

安卓:4.4.2

作者:254008829@qq.com

1.概述

此版飞思卡尔imx6SDK的内核是3.0.8还没有引入设备树,所以还是传统的platform_device和platform_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。
pdata是conststruct
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,则将它的值赋值给它,否则填-EINVAL。imx6引脚值都统一用宏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_type填ESDHC_CD_GPIO。
always_present:是否一直在卡槽里,或者一直和IMXCPU的SDIO连接,如果是则填1,否则为0。
support_8bit:这是一个关键的参数。一般SD卡是4bit数据线,EMMC是8bit数据线,如果是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数据项,这种和具体平台相关的数据有哪些呢,无非是控制器的iobase,irq的编号等等。层层解剖马上就见真面目了。
#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函数通过id,pdata。将一个和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));
}

此函数将iobase,irq等硬件资源生成一个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。将传入的res和pdata添加进入pdev成员里。最后调用platform_device_add(pdev)将之注册进linux系统。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: