Rockchip RK3399 - WiFi AP6356驱动
最初我的计划是从uboot开始讲起的,然后再来讲内核部分,但是在移植linux 5.2.8内核的时候发现其在支持WiFi模组AP6356时需要对内核代码进行大量的调整,不得不先过来研究一下AP6356驱动。
一、AP6356介绍
1.1 WiFi介绍
我们日常生活中,已经离不开WiFi,那到底是什么是WiFi?WiFi是一种基于IEEE 802.11标准的无线局域网技术,常用于在短距离范围内(例如建筑物、办公室或家庭)提供无线互联网接入。WiFi技术允许设备通过无线信号进行通信,例如智能手机、平板电脑、笔记本电脑、智能电视等设备都可以通过WiFi连接到互联网。
WiFi技术的实现主要依靠一系列硬件和软件设备,主要包括以下几个方面:
- 无线接入点:它是一个中央设备,通常被连接到网络的有线服务器或互联网服务提供商提供的调制解调器上。无线接入点可以转发数据流,也可以管理网络中的所有设备,并确保数据的安全性;
- 无线网卡:它是一种网络适配器,它通过将电脑或其他设备上的数据编码成一个无线信号,使得数据能够在无线网络上进行传输;
- 无线路由器:它是一个设备,将无线信号从无线网卡发送到无线接入点,并确定最佳的路由来实现无线数据传输;;
- 网络管理软件:这些软件可以辅助管理网络,例如配置无线接入点、设置网络加密等;
在实际应用中,这些硬件和软件设备通过遵循IEEE 802.11标准并使用无线电波来进行通信,从而实现了无线互联网的连接和数据传输。
1.1.1 WiFi模组
这里提到了WiFi模组,那和前面说的无线网卡、还有WiFi模块有啥区别呢?本质上,无论是无线网卡、还是WiFi模块、WiFi模组都是一种东西,就是提供无线上网的一种设备,但是从使用者的角度对其进行了具体的区分。
- 无线网卡:不同点在网卡,网卡分开就是网(上网)和卡(用于插入对应接口的插槽的集成电路电路板,也就是强调即插即用);
- WiFi模块:不同点在模块,这里的模块感觉来自于计算机系统设计过程中的功能模块和设备模块。换言之,WiFi模块,是计算机系统中用于无线上网的设备模块(这个设备模块往往集成了核心的无线通信芯片和相关外围电路),重点强调功能和模块化设计;
- WiFi模组:不同点在模组,这里的模组更强调器件的组装特性。换言之,无线模组,强调计算机系统硬件组装时的无线通信部分,重点强调硬件的组装;
WiFi功能在SoC中有两种方式:
- 集成在SoC中,芯片代表有MTK(联发科)和高通的芯片,因为这两家芯片厂商的通讯技术都比较厉害,所以一般都会在他们自己的ScC中添加WiFi的模块,从而降低成本和降低板子的面积;
- SoC通过外部接口连接WiFi模组,WiFi模组常用的通讯接口有SDIO、USB、PCIe、SPI、UART等;
常用的WiFi芯片厂商有NXP、瑞昱(Realtek)、博通Broadcom、MTK、Atheros等,然后一些模组厂商会使用这些公司的芯片来做一下WiFi模组,常见的模组厂商有富士康、海华、高盛达等等,模组厂商会调制好一些WiFi的芯片射频参数,写入模组当中,如果直接使用芯片在生产的时候需要调试WiFi的射频参数,而使用WiFi模组可以降低直接使用芯片的难度。
注意:模组和芯片有什么区别呢?模组通常是指将一个或多个芯片、电源管理电路、射频信号处理器、天线、外部连接器等封装在一个完整的系统中。这种设计可方便地集成到其他系统中,例如智能手机、物联网设备、手持设备等,以实现特定的功能和应用。更通俗的点说就是芯片是集成电路的一个核心,而模组是在芯片和其他外围电路之间提供完整系统解决方案的整合器。
1.1.2 WiFi蓝牙二合一硬件接口
为了降低成本,现在很多模组都是集成了WiFi和蓝牙功能的,所以我们不只看到有WiFi使用的SDIO接口,还有用于蓝牙通讯的UART接口,还有PCM接口。
我们播放歌曲的时候音乐的数据的传输使用的是UART接口,如果是蓝牙通话的时候使用的是PCM接口。所以我们可以在WiFi和蓝牙二合一的芯片上看到3种接口,其中SDIO是WiFi的,UART和PCM是蓝牙的。
我们使用的NanoPC-T4开发板搭载的就是一款集成了WiFi和蓝牙功能的二合一WiFi模组,型号为AP6356,基于Boardcom(博通)的WiFi芯片。
1.2 AP6356
AP6356是一款支持WiFi(2.4G/5G))+ BT蓝牙 4.1(2.4G)的模组,符合IEEE802.11a/b/g/n/ac 2x2 MIMO标准,并可在802.11n中通过双流达到867Mbps的速度以连接无线局域网。该集成模块提供SDIO/PCIe接口用于WiFi,UART/PCM接口用于蓝牙。
1.2.1 功能框图
AP6356功能框图如下所示,该图来自AP6356 datasheet:

AP6356一共包含了50个引脚,具体如下:
| 编号 | 名称 | 类型 | 描述 |
| 1 | GND | --- | Ground connections |
| 2 | WL/BT_ANT0 | I/O | RF I/O port0 |
| 3 | GND | --- | Ground connections |
| 4 | GND | --- | Ground connections |
| 5 | GND | --- | Ground connections |
| 6 | GND | --- | Ground connections |
| 7 | GND | --- | Ground connections |
| 8 | GND | --- | Ground connections |
| 9 | WL_ANT1 | I/O | RF I/O port1 |
| 10 | GND | --- | Ground connections |
| 11 | GND | --- | Ground connections |
| 12 | PCIE_PERST | I | PCIE system reset |
| 13 | XTAL_OUT | O | External Crystal out |
| 14 | XTAL_IN | I | External Crystal in/ Single clock source in |
| 15 | WL_REG_ON | I | Low asserting reset for WiFi core |
| 16 | WL_HOST_WAKE | O | WLAN to wake-up HOST |
| 17 | SDIO_DATA_CMD | I/O | SDIO command line |
| 18 | SDIO_DATA_CLK | I/O | SDIO clock line |
| 19 | SDIO_DATA_3 | I/O | SDIO data line 3 |
| 20 | SDIO_DATA_2 | I/O | SDIO data line 2 |
| 21 | SDIO_DATA_1 | I/O | SDIO data line 1 |
| 22 | SDIO_DATA_0 | I/O | SDIO data line 0 |
| 23 | GND | --- | Ground connections |
| 24 | PCIE_PME_L | O | PCIE power management event output |
| 25 | VIN_LDO | P | Internal Buck voltage generation pin |
| 26 | VIN_LDO_OUT | P | Internal Buck voltage generation pin |
| 27 | PCM_SYNC | I/O | PCM sync signal |
| 28 | PCM_IN | I | PCM data input |
| 29 | PCM_OUT | O | PCM Data output |
| 30 | PCM_CLK | I/O | PCM clock |
| 31 | LPO | I | External Low Power Clock input (32.768KHz) |
| 32 | GND | --- | Ground connections |
| 33 | PCIE_REFCLK_N | I | PCIE differential clock inputs 100MHz differential |
| 34 | VDDIO | P | I/O Voltage supply input |
| 35 | PCIE_REFCLK_P | I | PCIE differential clock inputs 100MHz differential |
| 36 | VBAT | P | Main power voltage source input |
| 37 | PCIE_CLKREQ_L | O | PCIE clock request signal |
| 38 | BT_REG_ON | I | Low asserting reset for Bluetooth core |
| 39 | GND | --- | Ground connections |
| 40 | UART_TXD | O | Bluetooth UART interface |
| 41 | UART_RXD | I | Bluetooth UART interface |
| 42 | UART_RTS_N | O | Bluetooth UART interface |
| 43 | UART_CTS_N | I | Bluetooth UART interface |
| 44 | PCIE_RDN | I | PCIE receiver differential pair |
| 45 | PCIE_RDP | I | PCIE receiver differential pair |
| 46 | PCIE_TDN | O | PCIE transmitter differential pair |
| 47 | PCIE_TDP | O | PCIE transmitter differential pair |
| 48 | GPIO8_9 | I | Mode selection, 1=PCIE mode , 0=SDIO mode |
| 49 | BT_WAKE | I | HOST wake-up Bluetooth device |
| 50 | BT_HOST_WAKE | O | Bluetooth device to wake-up HOST |
1.2.2 电源
AP6356需要两个涟源供应,VBAT、VDDIO;
- VBAT:输入供应电源,引脚编号为36;
- VDDIO:数字/蓝牙/SDIO/I/O电压,引脚编号为34;
1.2.3 SDIO引脚
AP6356支持SDIO 3.0版本,并且向下兼有SDIO2.0接口;其工作在1.8V、3.3V电压下(由VDDIO引脚决定),4位数据宽度;- 工作在1.8V电压下(超高速模式):SDR50(100Mbps)、SDR104(208MHz)、DDR50(50MHz);
- 工作在3.3V电压下(高速模式):默认速度(25MHZ)、高速(50MHZ);
需要注意的是:WiFi部分共有两部分供电组成,一个是主控端(即RK3399)的IO:CLK/CMD/D0~D3;另一个是WiFi模块的IO的供电,由WiFi模块中的VDDIO引脚供电,两部分供电必须一致否则会导致WiFi异常。
AP6356具有停止SDIO时钟和将中断信号映射到GPIO引脚的能力,WiFi模组想要打开SDIO接口时,会向主机发送 ‘out-of-band’中断信号,还提供了从WiFi模组内部强制控制门控时钟的能力。
以下是各个功能的说明:
- 功能0:标准SDIO功能,最大块大小/字节数=32B;
- 功能1:背板功能,用于访问内部SoC地址空间,最大块大小/字节数=64B;
- 功能2:WiFi功能,用于通过DMA进行高效的WiFi数据包传输,最大块大小/字节数=512B;
SDIO引脚描述:
|
SD 4-Bit Mode |
|
| DATA0 | Data Line 0 |
| DATA1 | Data Line 1 or Interrupt |
| DATA2 | Data Line 2 or Read Wait |
| DATA3 | Data Line 3 |
| CLK | Clock |
| CMD | Command Line |
此外与WiFi相关比较重要的引脚还有:
- WL_REG_ON:用于控制WiFi 核心的使能;
- WL_HOST_WAKE:可以通过向主机发送一个唤醒信号,使得主机低功耗模式(如睡眠模式、停止模式等)恢复到正常工作状态,这种唤醒信号通常会通过一些特定的接口(如 GPIO、中断线等)进行传输,WiFi模组会在需要唤醒主机时产生该信号;
- GPIO8_9:模式选择,0为SDIO模式,1位PCIe模式;
1.3 电路原理图
下图是我们使用的NanoPC-T4开发板AP6356的接线图:

在下图的右下角标注了VBAT电压范围为3.0~4.8V,而VDDIO输入电压为1.8V,也就是说SDIO工作于超高速模式。
1.3.1 WiFi相关引脚接线
这里我们需要关注一下RK3399与AP6356 WiFi相关引脚的接线:
| AP6356 | RK3399 | 其他 |
| PA_PU/PCIE_PERST | 悬空 | |
| XTAL_OUT | 37.4MHZ晶振 | |
| XTAL_IN | 37.4MHZ晶振 | |
| WL_REG_ON | WIFI_REG_ON_H(GPIO0_B2) | |
| WL_HOST_WAKE | WIFI_HOST_WAKE_L(GPIO0_A3) | |
| SDIO_DATA_CMD | SDIO0_CMD(GPIO2_D0) | |
| SDIO_DATA_CLK | SDIO0_CLK(GPIO2_D1) | |
| SDIO_DATA_3 | SDIO0_D3(GPIO2_C7) | |
| SDIO_DATA_2 | SDIO0_D2(GPIO2_C6) | |
| SDIO_DATA_1 | SDIO0_D1(GPIO2_C5) | |
| SDIO_DATA_0 | SDIO0_D0(GPIO2_C4) |
二、时序图
2.1 上电时序
AP6356有一些信号,允许主机通过启用或禁用蓝牙、WiFi和内部调节器块来控制功耗。下面描述了这些信号,此外,提供了图表以指示各种操作状态的正确时序。所示的时序值是最小要求值:更长的延迟也是可以接受的。
- WL_REG_ON:由PMU使用以开启或关闭WiFi功能使用的内部调节器;当这个引脚为高电平时,调节器被启用,WiFi功能退出复位状态。当这个引脚为低电平时,WLAN 部分被置于复位状态;
- BT_REG_ON:由PMU使用以开启或关闭用于BT功能的内部调节器;仅对蓝牙复位有效,不对WiFi产生影响,也不控制任何PMU功能。该引脚必须驱动到高电平或低电平(不能让其悬空);
2.1.1 WiFi开启、蓝牙开启

2.1.2 WiFi关闭、蓝牙关闭

2.1.3 WiFi开启、蓝牙关闭

2.1.3 WiFi关闭、蓝牙开启

2.2 SDIO时序
2.2.1 默认模式

2.2.2 高速模式

2.2.3 SDR模式


2.2.4 DDR50模式

三、设备树配置
SDIO总线和USB总线类似,SDIO也有两端,其中一端为主机(Host)端,另一端是设备端(Device),采用Host-Device这样的设计是为了简化Device的设计,所有的通信都由Host端发出命令开始、在Device端只要能解析Host发出的命令,就可以同Host进行通信了,SDIO的Host可以连接多个Device。
SDIO的驱动可以分为:
- SDIO主机控制器驱动:针对不同主机端的SDIO控制器的驱动;
- 主机端SDIO设备驱动:针对不同客户端的设备驱动程序。如SD卡、T-Flash卡、SDIO接口的GPS和WiFi等设备驱动;
3.1 MMC设备描述
3.1.1 新增sdio_pwrseq设备节点
修改arch/arm64/boot/dts/rockchip/rk3399-evb.dts,在根节点下新增sdio_pwrseq设备节点。sdio_pwrseq设备节点节点用于控制WiFi功能的开启和关闭。
sdio_pwrseq: sdio-pwrseq { compatible = "mmc-pwrseq-simple"; clocks = <&rk808 1>; clock-names = "ext_clock"; pinctrl-names = "default"; pinctrl-0 = <&wifi_enable_h>; /* * On the module itself this is one of these (depending * on the actual card populated): * - SDIO_RESET_L_WL_REG_ON * - PDN (power down when low) */ reset-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>; /* GPIO0_B2 */ };
其中属性:
- clocks:指定了设备使用的时钟源,即使用rk808时钟控制器ID为1的时钟,rk808是一个时钟提供者clock provider;
- clock-names:指定了所使用的时钟的名称,即 "ext_clock";
- pinctrl-names:设置了引脚的默认状态,引脚配置设置为wifi_enable_h;
- pinctrl-0:指定了default状态的对应的引脚配置,即wifi_enable_h;
- reset-gpios:指定wlan芯片所使用的的引脚为GPIO0_B2(这个引脚是gpio0控制器的第 10 个引脚,GPIO0_B2连接的是AP6356 的WL_REG_ON引脚),低电平有效,即低电平时会关闭WiFi功能;
sdio_pwrseq设备节点对应的驱动文件为drivers/mmc/core/pwrseq_simple.c,这个后面再来分析。
3.1.2 新增引脚配置节点
在介绍引脚配置节点之前,我们需要了解RK3399 GPIO的一些基础知识,RK3399共有5组GPIO口,依次为GPIO0~GPIO3;每一组又以A0~A7、B0~B7、C0~C7、D0~D7作为编号区分。
由于sdio_pwrseq节点配置了defalt状态对应的引脚配置节点为wifi_enable_h,因此需要在pinctrl节点下增加引脚配置节点wifi_enable_h:
sdio-pwrseq { wifi_enable_h: wifi-enable-h { rockchip,pins = <0 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>; }; };
其中属性rockchip,pins = <PIN_BANK PIN_BANK_IDX MUX &phandle>的意义如下:
- PIN_BANK:引脚所在的 bank;GPIO0~GPIO3依次对应0~3;
- PIN_BANK_IDX:引脚所在bank的引脚号;0~31,;
- MUX:功能复用配置,0 表示普通 GPIO,1-N 表示特殊的功能复用;
- phandle:引脚的电气特性,例如内部上拉、电流强度等;
因此此处配置GPIO0_B2引脚功能为GPIO,电气特性为pcfg_pull_none,表示普通配置;
更多关于节点属性含义含义参考:Documentation/devicetree/bindings/pinctrl/rockchip,pinctrl.txt;Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt。
3.1.3 新增sdio0设备节点属性
sdio0设备节点已经在arch/arm64/boot/dts/rockchip/rk3399.dtsi文件中定义,在根节点下已经定义了:
sdio0: dwmmc@fe310000 { compatible = "rockchip,rk3399-dw-mshc", "rockchip,rk3288-dw-mshc"; reg = <0x0 0xfe310000 0x0 0x4000>; interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH 0>; max-frequency = <150000000>; clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>, <&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>; clock-names = "biu", "ciu", "ciu-drive", "ciu-sample"; fifo-depth = <0x100>; power-domains = <&power RK3399_PD_SDIOAUDIO>; resets = <&cru SRST_SDIO0>; reset-names = "reset"; status = "disabled"; };
同时在pinctrl节点下定义了sdio0引脚配置节点:
sdio0 { sdio0_bus1: sdio0-bus1 { rockchip,pins = <2 RK_PC4 1 &pcfg_pull_up>; }; sdio0_bus4: sdio0-bus4 { rockchip,pins = <2 RK_PC4 1 &pcfg_pull_up>, <2 RK_PC5 1 &pcfg_pull_up>, <2 RK_PC6 1 &pcfg_pull_up>, <2 RK_PC7 1 &pcfg_pull_up>; }; sdio0_cmd: sdio0-cmd { rockchip,pins = <2 RK_PD0 1 &pcfg_pull_up>; }; sdio0_clk: sdio0-clk { rockchip,pins = <2 RK_PD1 1 &pcfg_pull_none>; }; sdio0_cd: sdio0-cd { rockchip,pins = <2 RK_PD2 1 &pcfg_pull_up>; }; sdio0_pwr: sdio0-pwr { rockchip,pins = <2 RK_PD3 1 &pcfg_pull_up>; }; sdio0_bkpwr: sdio0-bkpwr { rockchip,pins = <2 RK_PD4 1 &pcfg_pull_up>; }; sdio0_wp: sdio0-wp { rockchip,pins = <0 RK_PA3 1 &pcfg_pull_up>; }; sdio0_int: sdio0-int { rockchip,pins = <0 RK_PA4 1 &pcfg_pull_up>; }; };
这里我们需要在arch/arm64/boot/dts/rockchip/rk3399-evb.dts文件为sdio0设备节点新增属性:
&sdio0 { clock-frequency = <150000000>; clock-freq-min-max = <200000 150000000>; supports-sdio; bus-width = <4>; disable-wp; cap-sd-highspeed; cap-sdio-irq; keep-power-in-suspend; mmc-pwrseq = <&sdio_pwrseq>; non-removable; num-slots = <1>; pinctrl-names = "default"; pinctrl-0 = <&sdio0_bus4 &sdio0_cmd &sdio0_clk>; sd-uhs-sdr104; status = "okay"; };
3.2 WiFi功能描述
四、驱动分析
参考文章
[1] S3C2440上 MMC/SD卡驱动实例开发讲解
[2] NanoPC-T4开发板原理图
[3] RK3399教程:wifi驱动调试技巧
[4] Rockchip基于RK3566/RK3568 WiFi AP6256调试笔记
[5] RK3399驱动开发 | 13 - AP6356 SDIO WiFi 调试(基于linux4.4.194内核)
[6] Linux SD卡/SDIO驱动开发0-基本知识
[7] Linux-MMC子系统
[8] RK3399 GPIO 配置与使用