imx6 android4.4.2内核sd卡platform_device注册加载原理
2016-04-08 17:58
489 查看
单板环境:
linux内核:3.0.8
安卓:4.4.2
作者:254008829@qq.com
imx6q_add_sdhci_usdhc_imx是一个宏函数,它有两个参数,@arg1:id,@arg2:pdata。
id表示sdio的通道号,如我们单板SDIO1(从1开始数)外接的是SD卡,其对应的id等于0。
pdata是conststruct
esdhc_platform_data类型的指针。
wp_gpio:如果外设芯片有写保护引脚接到IMXCPU,则将它的值赋值给它,否则填-EINVAL。imx6引脚值都统一用宏IMX_GPIO_NR来定义,如GPIO_5_7,则其值为IMX_GPIO_NR(5,7)。
cd_gpio:如果外设是有中断检测引脚来检测外设是否插入,则需要给此成员赋值。典型的应用是SD卡,一般都是需要支持热插拔的,有中断检测引脚。将引脚值用IMX_GPIO_NR来定义。
cd_type:
如果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变量,如下:
调用函数将之注册:imx6q_add_sdhci_usdhc_imx(0,&mx6q_sabresd_sd1_data);
#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是干什么的?
往后追代码:
结构数据中对应结构的定义为:
imx_sdhci_usdhc_imx_data_entry这个宏从字面上来看就是imxsd数据项,这种和具体平台相关的数据有哪些呢,无非是控制器的iobase,irq的编号等等。层层解剖马上就见真面目了。
至此总结下: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。
此函数将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函数定义:
这函数比较容易,定义一个structplatform_device类型的指针pdev。将传入的res和pdata添加进入pdev成员里。最后调用platform_device_add(pdev)将之注册进linux系统。
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系统。
相关文章推荐
- 获取Android设备的唯一 Android ID
- Android4.3 蓝牙BLE初步
- Android安全攻防战,反编译与混淆技术完全解析(上)
- 解决android控件事件冲突
- Android开发请求网络方式详解
- Android 字体设置-Typeface讲解
- Android客户端和服务器数据交互
- 计算缓存大小并且清空缓存 && Android存储访问及目录
- Android Studio---Error:(18, 0) Gradle DSL method not found: 'android()'
- Android 自定义加载中
- Android中的windowSoftInputMode属性详解
- Android —— 实现推送方式解决方案
- Android Studio插件GsonFormat快速实现JavaBean
- android组件之DrawerLayout(抽屉导航)-- 侧滑菜单效果
- 关于在android程序执行过程中使用Intent启动另一个活动后,同个方法未执行的代码是否会继续执行。
- android中关于文件解码时出现乱码的相关分析
- Android上传图片之调用系统拍照和从相册选择图片
- Android 恢复Activity现场
- Android Studio老是出现Cannot reload AVD list: for input string: ""这个错误
- Android MediaPlayer实现音乐播放