近期需要把WiFi无线网络功能移植到在iTOP4412 开发平台,查阅了相关资料,经过一段时间的研究、调试,终于成功,将WiFi功能移植到了开发板上面,这里笔者记录移植过程及注意事项,方便以后工作需要。
iTOP4412开发板的WiFi模块与板卡之间的连接采用SDIO接口,WiFi硬件模块使用的是MTK的MT6620芯片,MTK提供了Android4.0及Android4.4的driver, Porting Guid,有了这些就为我们的移植工作做了总体性的指导。
但是仅仅有MTK提供的文档还是远远不够的,毕竟硬件接口定义不同,kernel版本也不同,Android层与MTK提供的代码也有差异,这就需要我们在MTK文档的指导下, Step by Step 进行 Porting 工作.
移植环境:
1 iTOP4412 精英版 + MT6620 WiFi模块
2 kernel 3.0.15 version
3 Android4.4.4
4 Ubuntu12.04 64BIt 开发环境
1.2 硬件相关部分
下图为WiFi模块与开发板连接的引脚定义,通过该接口可以看出WiFi模块与CPU的交互接口.
查看WiFi模块的原理图可知,WiFi模块与CPU之间采用SDIO接口进行数据和命令的交互工作,Pin1,2,3,5,6,7 为SDIO接口,另外还需要Pin8,Pin10 UART 串口, 另外Pin18,Pin17用于串口流控,实际是可以不使用流控功能。 MT6620芯片是复合芯片,除了具备WiFi功能以后,还支持蓝牙,FM,GPS功能,他们与CPU之间的通信需要串口,另外MT6620固件补丁的下载也是通过串口进行的,所以说串口是必不可少的硬件接口,即使您只使用该芯片的WiFi功能。
除了SDIO接口,UART口以外,还需要与CPU进行交互的接口包括Pin16 PMU_EN ,使能引脚,默认低电平状态,高电平有效。
Pin15 WiFI_RESET引脚,默认低电平状态,高电平有效,用于复位WiFi芯片;
Pin 13 WiFi INT WiFI的中断引脚,用于告知CPU有数据来了;
以上这些引脚需要在BSP中配置,WiFI的驱动会通过这些引脚与MT6620芯片进行通信;
另外需要强调的是WiFi模块占用CPU的MMC3端口, 也可以说就是SDIO总线,关于MMC,SD,SDIO总线的来历这里不再描述,下图为核心板WiFi相关部分:
在iTOP4412 精英版中 mmc0,mmc1总线用于eMMC,mmc2用于TF卡,mmc3用于SDIO WiFi,关于wifi的其他硬件连接比如串口,使能(GPIO),复位(GPIO),中断(GPIO)这里不再贴出,具体的请查看开发板的原理图。
这里重点强调一下 HUB_CONNECT,也就是上图中的L5 引脚,该引脚复用为mmc总线的CD引脚,也就是Card Detect引脚,用于告知MMC3控制器有设备插入,属于中断引脚,我们需要把引脚与CPU的 某GPIO引脚连接:
以上为iTOP4412 底板原理图WiFi接口部分,请注意HUB_CONNECT引脚通过0欧电阻与6060_GPIO2进行了连接,6060_GPIO2为核心板GPIO引脚。
这样连接的目的是通过软件输出6060_GPIO2低电平,从而HUB_CONNECT引脚为低电平输入状态,MMC3控制认为有设备插入到了MMC3总线上面,原理同TF卡,SD卡的检测。
以上为进行Porting前的准备工作,当然需要万用表,示波器工具进行辅助的检测,查看WiFi模块的工作电压是否正常,GPIO的当前状态,MMC总线上面的时钟及是否有数据从MMC3控制器输出等等.
1.3 Kernel
1.3.1 概述
iTOP4412开发板采用的是Linux 3.0.15 版本,MTK官方给的移植Porting没有说明针对具体的 kernel版本,由于是Android4.4,所以kernel应该是3.0以后的版本或者更高支持;
首先按照PoringGuid的指导说明,把New和Modify文件夹下面关于kernel部分的修改放到我们的kernel代码里面,MT6620的驱动分两个部分, 一部分放在driver/misc/目录下面,文件夹名称 mediatek,里面存放的是WMT,既wireless manage tools, 里面提供了与MT6620 download firmware patch ,enable /disable WIFi芯片,power on, power off操作的相关驱动部分, 及SDIO总线设备接口驱动Host Interface drivers,这些驱动工作正常后才开始加载WiFi 网络相关驱动。
我们以驱动库 .ko 的形式编译驱动模块,driver/misc/mediatek/ 库文件与WiFi网络库文件 列表如下::
mtk_hif_sdio.ko ----mmc总线相关接口,mmc总线发现SDIO设备,分配总线地址后,会与该驱动进行适配.适配成功后该驱动会调用WiFi网络驱动;
mtk_stp_uart.ko-----串口相关驱动,通过串口下载固件补丁,设置芯片参数;
mtk_stp_wmt.ko-----core 部分,提供WiFi上电,断电等等相关操作;
mtk_wmt_wifi.ko ----字符设备驱动,创建设备节点用于与用户空间交互;
wlan_mt6620.ko -- -- WiFi网络相关驱动,不需要我们进行修改;
另外这里附一张MMC驱动框架图:
因为我们的MT6620模块挂载到了MMC总线上面,属于MMC子系统的工作实例,我们非常有必要熟悉一下mmc驱动架构,是我们移植工作的非常重要的一部分,关于MMC子系统的详细介绍这里不再说明.
1.3.2 代码修改
1 根据硬件连接情况配置必要的平台资源
修改文件: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
关键函数1: 该函数配置WiFi相关的GPIO引脚为初始化输出状态,或者配置为中断状态
WIFI驱动会改变这些引脚的状态,这里仅仅是初始化.
static void __init mtk_combo_init(void)
{
//MT66XX PMUEN
if(gpio_request(EXYNOS4_GPC1(0), "GPC1_0"))
{
printk(KERN_ERR "failed to request GPC1_0 for MT6620 PMUEN control\n");
}
//MT66XX SYSRST
if(gpio_request(EXYNOS4_GPC1(1), "GPC1_1"))
{
printk(KERN_ERR "failed to request GPC1_1 for MT6620 SYSRST control\n");
}
s3c_gpio_cfgpin(EXYNOS4_GPC1(0), S3C_GPIO_OUTPUT);
s3c_gpio_cfgpin(EXYNOS4_GPC1(1), S3C_GPIO_OUTPUT);
gpio_direction_output(EXYNOS4_GPC1(0), 0);
gpio_direction_output(EXYNOS4_GPC1(1), 0);
gpio_free(EXYNOS4_GPC1(0));
gpio_free(EXYNOS4_GPC1(1));
mdelay(5);
//need config eint models for Wifi & BGA Interupt
if (gpio_request(EXYNOS4_GPX2(5), "WiFi INT"))
printk(KERN_WARNING "MT6620 WiFi INT(GPX2.5) Port request error!!!\n");
else {
s3c_gpio_setpull(EXYNOS4_GPX2(5), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(EXYNOS4_GPX2(5), S3C_GPIO_SFN(0xF));
gpio_free(EXYNOS4_GPX2(5));
}
if (gpio_request(EXYNOS4_GPX2(4), "BGF INT"))
printk(KERN_WARNING "MT6620 BGA INT(GPX2.4) Port request error!!!\n");
else {
s3c_gpio_setpull(EXYNOS4_GPX2(4), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(EXYNOS4_GPX2(4), S3C_GPIO_SFN(0xF));
gpio_free(EXYNOS4_GPX2(4));
}
//normal it is high level
if (gpio_request(EXYNOS4_GPX3(2), "6260_GPIO2")!=0) {
printk("[mt6620] ERROR:Cannot request 6260_GPIO2\n");
} else {
gpio_direction_output(EXYNOS4_GPX3(2), 1);/* WLAN_CHIP_PWD */
gpio_set_value(EXYNOS4_GPX3(2), 1);
mdelay(100);
gpio_free(EXYNOS4_GPX3(2));
}
return; }
关键函数2: setup_mt6620_wlan_power_for_onoff
该函数为导出函数,WIFi驱动会调用该函数,该函数关键地方是让MMC控制器驱动扫描MMC总线上面的设备,MMC扫描到了WiFI模块才会加载相应的WiFi驱动,这里是主动让MMC扫描,我们的SD卡是采用中断触发的方式扫描,他们本质上都是扫描MMC总线上面的新设备,然后加载对应的设备驱动,具体的可以看一下MMC子系统相关内容.
函数所属文件: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
void setup_mt6620_wlan_power_for_onoff(int on)
{
int chip_pwd_low_val;
int outValue;
printk("[mt6620] +++ %s : wlan power %s\n",__func__, on?"on":"off");
#if 1
if (on) {
outValue = 0;
} else {
outValue = 1;
}
if (gpio_request(EXYNOS4_GPX3(2), "6260_GPIO2")!=0) {
printk("[mt6620] ERROR:Cannot request 6260_GPIO2\n");
} else {
gpio_direction_output(EXYNOS4_GPX3(2), 1);/* WLAN_CHIP_PWD */
gpio_set_value(EXYNOS4_GPX3(2), outValue);
mdelay(100);
gpio_free(EXYNOS4_GPX3(2));
}
if(on)
{
//need reset on mt6620 ? need test......
}
#endif
extern void sdhci_s3c_sdio_card_detect(struct platform_device *pdev);
// mdelay(200);
//need sdhc controler check wifi catd states......
sdhci_s3c_sdio_card_detect(&s3c_device_hsmmc3);
printk("[mt6620] --- %s\n",__func__);
}
EXPORT_SYMBOL(setup_mt6620_wlan_power_for_onoff);
关键结构体: 该结构体告诉WiFi驱动相关部分使用了平台的哪些GPIO资源.
结构体所属文件: kernel/iTop4412_Kernel_3.0/arch/arm/mach-exynos/mach-itop4412.c
static struct mtk_wmt_platform_data mtk_wmt_pdata = {
.pmu =EXYNOS4_GPC1(0), //RK30SDK_WIFI_GPIO_POWER_N,//RK30_PIN0_PB5, //MUST set to pin num in target system
.rst = EXYNOS4_GPC1(1),//RK30SDK_WIFI_GPIO_RESET_N,//RK30_PIN3_PD0, //MUST set to pin num in target system
.bgf_int=EXYNOS4_GPX2(4), //IRQ_EINT(20),//RK30SDK_WIFI_GPIO_BGF_INT_B,//RK30_PIN0_PA5,//MUST set to pin num in target system if use UART interface.
.urt_cts = -EINVAL, // set it to the correct GPIO num if use common SDIO, otherwise set it to -EINVAL.
.rtc = -EINVAL, //Optipnal. refer to HW design.
.gps_sync = -EINVAL, //Optional. refer to HW design.
.gps_lna = -EINVAL, //Optional. refer to HW design.
};
static struct mtk_sdio_eint_platform_data mtk_sdio_eint_pdata = {
.sdio_eint = EXYNOS4_GPX2(5),//IRQ_EINT(21) ,//RK30SDK_WIFI_GPIO_WIFI_INT_B,//53, //MUST set pin num in target system.
};
static struct platform_device mtk_wmt_dev = {
.name = "mtk_wmt",
.id = 1,
.dev = {
.platform_data = &mtk_wmt_pdata,
},
};
static struct platform_device mtk_sdio_eint_dev = {
.name = "mtk_sdio_eint",
.id = 1,
.dev = {
.platform_data = &mtk_sdio_eint_pdata,
},
};