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

来源:互联网 发布:linux ftp 指定目录 编辑:程序博客网 时间:2024/05/18 20:34
单板环境:
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.cmx6_sabresd_board_inimx6q_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变量,如下:

// sdcardstatic 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 0
原创粉丝点击