Rockchip RK3399 - WiFi AP6356驱动
----------------------------------------------------------------------------------------------------------------------------
开发板 :NanoPC-T4开发板
eMMC :16GB
LPDDR3 :4GB
显示屏 :15.6英寸HDMI接口显示屏
u-boot :uboot 2023.04
linux :6.3、5.2.8
----------------------------------------------------------------------------------------------------------------------------
最初我的计划是从uboot开始讲起的,然后再来讲内核部分,但是在移植linux 6.3内核的时候发现其在支持WiFi模组AP6356时需要对内核代码进行大量的调整,不得不先过来研究一下AP6356驱动。
需要注意的是:本篇博客是以linux 6.3版本为主体介绍的,但是内容同样适用于linux 5.2.8版本。
一、AP6356介绍
1.1 WiFi介绍
我们日常生活中,已经离不开WiFi,那到底是什么是WiFi?WiFi是一种基于IEEE 802.11标准的无线局域网技术,常用于在短距离范围内(例如建筑物、办公室或家庭)提供无线互联网接入。WiFi技术允许设备通过无线信号进行通信,例如智能手机、平板电脑、笔记本电脑、智能电视等设备都可以通过WiFi连接到互联网。
WiFi技术的实现主要依靠一系列硬件和软件设备,主要包括以下几个方面:
- 无线接入点:它是一个中央设备,通常被连接到网络的有线服务器或互联网服务提供商提供的调制解调器上。无线接入点可以转发数据流,也可以管理网络中的所有设备,并确保数据的安全性;
- 无线网卡:它是一种网络适配器,它通过将电脑或其他设备上的数据编码成一个无线信号,使得数据能够在无线网络上进行传输;
- 无线路由器:它是一个设备,将无线信号从无线网卡发送到无线接入点,并确定最佳的路由来实现无线数据传输;;
- 网络管理软件:这些软件可以辅助管理网络,例如配置无线接入点、设置网络加密等;
在实际应用中,这些硬件和软件设备通过遵循IEEE 802.11标准并使用无线电波来进行通信,从而实现了无线互联网的连接和数据传输。
这里顺带说一下WLAN,WLAN英文全称是Wireless Local Area Networks,无线局域网络。WiFi是实现WLAN的一种技术。
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:当WiFi部分接收到数据时,可以通过中断唤醒CPU;
- GPIO8_9:模式选择,0为SDIO模式,1位PCIe模式;
1.3 电路原理图
下图是我们使用的NanoPC-T4开发板AP6356的接线图:
需要注意的是:
- 在下图的右下角标注了VBAT电压范围为3.0~4.8V,输入电压源来自VCC3V3_SYS,这个信号是由电源输入的12V电源经过稳压管NB680GD输出得到的;
- VDDIO输入电压为1.8V,输入电压来自rk808电源管理芯片61号引脚输出的;
- LPO(外部低功耗时钟输入)输入引脚连接的是RTC_CLKO_WIFI,而该引脚来自rk808电源管理芯片67号输出引脚CLK32KOUT2;
1.3.1 WiFi相关引脚接线
这里我们需要关注一下RK3399与AP6356 WiFi相关引脚的接线:
AP6356 | RK3399 | 其他 | 功能 |
XTAL_OUT | 37.4MHZ晶振 | ||
XTAL_IN | 37.4MHZ晶振 | ||
WL_REG_ON | WIFI_REG_ON_H(GPIO0_B2) | 开启/关闭WiFi | |
WL_HOST_WAKE | WIFI_HOST_WAKE_L(GPIO0_A3) | WiFi设备唤醒主机 | |
SDIO_DATA_CMD | SDIO0_CMD(GPIO2_D0) | SDIO Command Line | |
SDIO_DATA_CLK | SDIO0_CLK(GPIO2_D1) | SDIO Clock | |
SDIO_DATA_3 | SDIO0_D3(GPIO2_C7) | Data Line 3 | |
SDIO_DATA_2 | SDIO0_D2(GPIO2_C6) | Data Line 2 or Read Wait | |
SDIO_DATA_1 | SDIO0_D1(GPIO2_C5) | Data Line 1 or Interrupt | |
SDIO_DATA_0 | SDIO0_D0(GPIO2_C4) | Data Line 0 | |
LPO | rk808电源管理芯片67号输出引脚CLK32KOUT2 | 外部低功耗时钟输入 |
二、时序图
2.1 上电时序
AP6356有一些信号,允许主机通过启用或禁用蓝牙、WiFi来控制功耗。下面描述了这些信号,此外,提供了图表以指示各种操作状态的正确时序。所示的时序值是最小要求值:更长的延迟也是可以接受的。
- WL_REG_ON:开启和关闭WiFi功能;
- BT_REG_ON:开启和关闭蓝牙功能,该引脚必须驱动到高电平或低电平(不能让其悬空);
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等设备驱动;
而WiFi设备驱动实际上又涉及到多个驱动模块,比如rkfill驱动,802.11模块驱动、以及WiFI设备自身驱动。
3.1 rfkill驱动
rfkill驱动是和平台SoC有关联的的,一般是SoC厂家提供,这部分的功能是给RF设备(无线通信设备、比如蓝牙、WiFi)上下电使用,比如唤醒引脚配置,当RF设备接收到数据的时候用来唤醒系统,比如上下电引脚配置,用于开启/禁用RF设备,这部分的功能就集成在rfkill-wlan.c、rfkill-bt.c。
关于rfkill 蓝牙驱动驱动、rfkill WiFi驱动源码分析参考Rockchip RK3399 - rfkill子系统。
3.2 WiFI设备自身驱动
WiFI设备自身驱动一般都是由WiFi芯片厂家提供,比如WiFi芯片厂商瑞昱、博通;这部分是标准的基本不用修改,不管是NXP、Rockchip、全志等平台都是使用这一套代码。
四、设备树以及源码调整
由于linux 6.3版本并没有直接支持AP6356,因此为了支持AP6356这里需要修改的内容比较多,因为这涉及到到了源码以及设备树的调整。
4.1 MMC驱动配置
4.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,这个后面再来分析。
4.1.2 新增wifi_enable_h引脚配置节点
在介绍引脚配置节点之前,我们需要了解RK3399 GPIO的一些基础知识,RK3399共有5组GPIO口,依次为GPIO0~GPIO4;每一组又以A0~A7、B0~B7、C0~C7、D0~D7作为编号区分。
由于sdio_pwrseq节点配置了default状态对应的引脚配置节点为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。
4.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"; };
4.2 WiFi驱动配置
Rockchip官方平台linux版本(4.4、4.19、5.10)实现了一套兼容多款WiFi芯片的自适应框架,位于drivers/net/wireless/rockchip_wlan路径下,简单来说就是将多款WiFi芯片的驱动编译成xxx.ok文件,比如常用的WiFi芯片厂商瑞昱、博通的WiFi芯片的驱动统一编译成不同的xxx.ko文件。
但是我们使用的Linux 6.3版本并没有drivers/net/wireless/rockchip_wlan这个文件夹,因此我们要不将这个文件夹从Rockchip官方平台linux版本拷贝过来(拷贝过来后需要进行配置、源码调整,不然编译不过),要不然只能另辟蹊径。
AP6356使用的是博通BCM4356方案,所以在linux中,使能brcmfmac模块驱动即可;如果不知道使用的博通哪种方案,可以先不配置固件,在内核启动的时候会提示缺少哪一种固件,比如linux 5.2.8版本错误:
[ 4.087532] brcmfmac: brcmf_fw_alloc_request: using brcm/brcmfmac4356-sdio for chip BCM4356/2 [ 4.097518] brcmfmac mmc0:0001:1: Direct firmware load for brcm/brcmfmac4356-sdio.bin failed with error -2
linux 6.3版本错误:
[ 831.957933] brcmfmac: brcmf_fw_alloc_request: using brcm/brcmfmac4356-sdio for chip BCM4356/2 [ 831.968205] brcmfmac mmc1:0001:1: Direct firmware load for brcm/brcmfmac4356-sdio.rockchip,rk3399-evb.bin failed with error -2 [ 832.083413] brcmfmac: brcmf_fwvid_request_module: mod=wcc: failed 256 [ 832.090694] ieee80211 phy0: brcmf_attach: brcmf_fwvid_attach failed [ 832.121230] brcmfmac: brcmf_sdio_firmware_callback: brcmf_attach failed
从2.6.24开始,内核收录的是b43驱动,该驱动是当初博通的驱动没有开源,有大佬通过逆向工程得到的驱动源码;
从2.6.39开始,博通完全开源了硬件驱动,内核发布版本中包含此驱动,分为brcmsmac和brcmfmac;
- brcmfmac:提供原生硬MAC支持,支持较新的芯片支持,支持AP模式、P2P模式、高级加密;
- brcmsmac:提供基于mac802.11的软MAC支持,仅支持较老的芯片,比如BCM4313、BCM43224、BCM43225;
驱动文件在drivers/net/wireless/broadcom目录下;
root@zhengyang:/work/sambashare/rk399/linux-6.3# ll drivers/net/wireless/broadcom 总用量 28 drwxr-xr-x 5 root root 4096 Jun 4 14:10 ./ drwxr-xr-x 18 root root 4096 May 17 01:26 ../ drwxr-xr-x 2 root root 4096 May 17 01:26 b43/ drwxr-xr-x 2 root root 4096 May 17 01:26 b43legacy/ drwxr-xr-x 6 root root 4096 May 17 01:26 brcm80211/ -rw-r--r-- 1 root root 657 May 17 01:26 Kconfig -rw-r--r-- 1 root root 181 May 17 01:26 Makefile root@zhengyang:/work/sambashare/s3c2440/linux-6.3# ll drivers/net/wireless/broadcom/brcm80211/ 总用量 32 drwxr-xr-x 6 root root 4096 Jun 4 14:19 ./ drwxr-xr-x 5 root root 4096 Jun 4 14:14 ../ drwxr-xr-x 2 root root 4096 May 17 01:26 brcmfmac/ drwxr-xr-x 3 root root 4096 May 17 01:26 brcmsmac/ drwxr-xr-x 2 root root 4096 May 17 01:26 brcmutil/ drwxr-xr-x 2 root root 4096 May 17 01:26 include/ -rw-r--r-- 1 root root 2771 May 17 01:26 Kconfig -rw-r--r-- 1 root root 1001 May 17 01:26 Makefile
4.2.1 配置brcmfmac驱动
需要配置内核支持brcmfmac驱动:
Device Drivers ---> [*] Network device support ---> [*] Wireless LAN ---> [*] Broadcom devices < > Broadcom 43xx wireless support (mac80211 stack) # CONFIG_B43 < > Broadcom 43xx-legacy wireless support (mac80211 stack) # CONFIG_B43LEGACY < > Broadcom IEEE802.11n PCIe SoftMAC WLAN driver # CONFIG_BRCMSMAC 支持较老的芯片 [M] Broadcom FullMAC WLAN driver # CONFIG_BRCMFMAC 支持较新的芯片 [*] SDIO bus interface support for FullMAC driver # CONFIG_BRCMFMAC_SDIO [ ] USB bus interface support for FullMAC driver # CONFIG_BRCMFMAC_USB [*] Broadcom device tracing # CONFIG_BRCM_TRACING [*] Broadcom driver debug functions # CONFIG_BRCMDBG
由于brcmfmac驱动会加载固件/lib/firmware/brcm/brcmfmac4356-sdio.bin,因此这里只能将Broadcom FullMAC WLAN driver编译成模块。
如果编译进内核,由于加载固件在根文件系统挂载之前,导致出现无法找到固件文件的错误。
4.2.2 配置IEEE 802.11驱动
需要配置内核支持IEEE 802.11驱动:
[*] Networking support --->
-*- Wireless --->
<M> cfg80211 - wireless configuration API
cfg80211.ko驱动模块同样需要加载固件/lib/firmware/regulatory.db,因此这里也只能将cfg80211 - wireless configuration API编译成模块。
4.2.3 固件下载
打开linux根目录下drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c文件,搜索4356,定位到:
BRCMF_FW_DEF(43143, "brcmfmac43143-sdio"); BRCMF_FW_DEF(43241B0, "brcmfmac43241b0-sdio"); BRCMF_FW_DEF(43241B4, "brcmfmac43241b4-sdio"); BRCMF_FW_DEF(43241B5, "brcmfmac43241b5-sdio"); BRCMF_FW_DEF(4329, "brcmfmac4329-sdio"); BRCMF_FW_DEF(4330, "brcmfmac4330-sdio"); BRCMF_FW_DEF(4334, "brcmfmac4334-sdio"); BRCMF_FW_DEF(43340, "brcmfmac43340-sdio"); BRCMF_FW_DEF(4335, "brcmfmac4335-sdio"); BRCMF_FW_DEF(43362, "brcmfmac43362-sdio"); BRCMF_FW_DEF(4339, "brcmfmac4339-sdio"); BRCMF_FW_DEF(43430A0, "brcmfmac43430a0-sdio"); /* Note the names are not postfixed with a1 for backward compatibility */ BRCMF_FW_CLM_DEF(43430A1, "brcmfmac43430-sdio"); BRCMF_FW_DEF(43430B0, "brcmfmac43430b0-sdio"); BRCMF_FW_CLM_DEF(43439, "brcmfmac43439-sdio"); BRCMF_FW_CLM_DEF(43455, "brcmfmac43455-sdio"); BRCMF_FW_DEF(43456, "brcmfmac43456-sdio"); BRCMF_FW_CLM_DEF(4354, "brcmfmac4354-sdio"); BRCMF_FW_CLM_DEF(4356, "brcmfmac4356-sdio"); BRCMF_FW_DEF(4359, "brcmfmac4359-sdio"); BRCMF_FW_CLM_DEF(4373, "brcmfmac4373-sdio"); BRCMF_FW_CLM_DEF(43012, "brcmfmac43012-sdio"); BRCMF_FW_CLM_DEF(43752, "brcmfmac43752-sdio");
可以看到4356对应的固件文件为brcmfmac4356-sdio.bin。固件使用armbian提供的:https://github.com/armbian/firmware/tree/master/brcm;
我们把文件全部下载下来,这里面也包含了固件firmware/regulatory.db,下载完成后在内核启动后我们通过scp命令将这些文件拷贝到开发板根文件系统的/lib/目录中;
root@zhengyang:/work/sambashare/rk3399/linux-6.3# git clone https://github.com/armbian/firmware.git --depth 1
4.3 rfkill驱动配置
4.3.1 源码拷贝
将Rockchip官方linux 5.10版本net/rfkill/rfkill-bt.c、net/rfkill/rfkill-wlan.c、rfkill-bt.h、rfkill-wlan.h等文件复制到linux 6.3 net/rfkill路径下;
root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp ../rockchip/linux-5.10/net/rfkill/rfkill-bt.c ./net/rfkill/ root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp ../rockchip/linux-5.10/net/rfkill/rfkill-wlan.c ./net/rfkill/ root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp ../rockchip/linux-5.10/include/linux/rfkill-bt.h ./include/linux/ root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp ../rockchip/linux-5.10/include/linux/rfkill-wlan.h ./include/linux/ root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp ../rockchip/linux-5.10/include/linux/wakelock.h ./include/linux/ root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp -R ../rockchip/linux-5.10/include/linux/rockchip ./include/linux/ root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp -R ../rockchip/linux-5.10/include/linux/soc/rockchip ./include/linux/soc
(1) linux 5.2.8
对于linux 5.2.8版本需要替换net/rfkill/rfkill-bt.c内容如下(来自Rockchip linux 4.19版本):
/* * Copyright (C) 2012 ROCKCHIP, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ /* Rock-chips rfkill driver for bluetooth * */ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/module.h> #include <linux/rfkill.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/rfkill-bt.h> #include <linux/rfkill-wlan.h> #include <linux/wakelock.h> #include <linux/interrupt.h> #include <asm/irq.h> #include <linux/suspend.h> #include <linux/proc_fs.h> #include <linux/uaccess.h> #include <linux/gpio.h> #include <linux/fs.h> #include <dt-bindings/gpio/gpio.h> #include <uapi/linux/rfkill.h> #ifdef CONFIG_OF #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> #endif #if 0 #define DBG(x...) pr_info("[BT_RFKILL]: " x) #else #define DBG(x...) #endif #define LOG(x...) pr_info("[BT_RFKILL]: " x) #define BT_WAKEUP_TIMEOUT 10000 #define BT_IRQ_WAKELOCK_TIMEOUT (10 * 1000) #define BT_BLOCKED true #define BT_UNBLOCK false #define BT_SLEEP true #define BT_WAKEUP false enum { IOMUX_FNORMAL = 0, IOMUX_FGPIO, IOMUX_FMUX, }; struct rfkill_rk_data { struct rfkill_rk_platform_data *pdata; struct platform_device *pdev; struct rfkill *rfkill_dev; struct wake_lock bt_irq_wl; struct delayed_work bt_sleep_delay_work; int irq_req; }; static struct rfkill_rk_data *g_rfkill = NULL; static const char bt_name[] = #if defined(CONFIG_BCM4330) #if defined(CONFIG_BT_MODULE_NH660) "nh660" #else "bcm4330" #endif #elif defined(CONFIG_RK903) #if defined(CONFIG_RKWIFI_26M) "rk903_26M" #else "rk903" #endif #elif defined(CONFIG_BCM4329) "bcm4329" #elif defined(CONFIG_MV8787) "mv8787" #elif defined(CONFIG_AP6210) #if defined(CONFIG_RKWIFI_26M) "ap6210" #else "ap6210_24M" #endif #elif defined(CONFIG_AP6330) "ap6330" #elif defined(CONFIG_AP6476) "ap6476" #elif defined(CONFIG_AP6493) "ap6493" #elif defined(CONFIG_AP6441) "ap6441" #elif defined(CONFIG_AP6335) "ap6335" #elif defined(CONFIG_GB86302I) "gb86302i" #else "bt_default" #endif ; static irqreturn_t rfkill_rk_wake_host_irq(int irq, void *dev) { struct rfkill_rk_data *rfkill = dev; LOG("BT_WAKE_HOST IRQ fired\n"); DBG("BT IRQ wakeup, request %dms wakelock\n", BT_IRQ_WAKELOCK_TIMEOUT); wake_lock_timeout(&rfkill->bt_irq_wl, msecs_to_jiffies(BT_IRQ_WAKELOCK_TIMEOUT)); return IRQ_HANDLED; } static int rfkill_rk_setup_gpio(struct platform_device *pdev, struct rfkill_rk_gpio *gpio, const char *prefix, const char *name) { if (gpio_is_valid(gpio->io)) { int ret = 0; sprintf(gpio->name, "%s_%s", prefix, name); ret = devm_gpio_request(&pdev->dev, gpio->io, gpio->name); if (ret) { LOG("Failed to get %s gpio.\n", gpio->name); return -1; } } return 0; } static int rfkill_rk_setup_wake_irq(struct rfkill_rk_data *rfkill, int flag) { int ret = 0; struct rfkill_rk_irq *irq = &rfkill->pdata->wake_host_irq; if (!flag) { rfkill->irq_req = 0; ret = rfkill_rk_setup_gpio(rfkill->pdev, &irq->gpio, rfkill->pdata->name, "wake_host"); if (ret) goto fail1; } if (gpio_is_valid(irq->gpio.io)) { if (rfkill->irq_req) { rfkill->irq_req = 0; free_irq(irq->irq, rfkill); } LOG("Request irq for bt wakeup host\n"); irq->irq = gpio_to_irq(irq->gpio.io); sprintf(irq->name, "%s_irq", irq->gpio.name); ret = request_irq(irq->irq, rfkill_rk_wake_host_irq, (irq->gpio.enable == GPIO_ACTIVE_LOW) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING, irq->name, rfkill); if (ret) goto fail2; rfkill->irq_req = 1; LOG("** disable irq\n"); disable_irq(irq->irq); /*ret = disable_irq_wake(irq->irq);init irq wake is disabled,no need to disable*/ } return ret; fail2: gpio_free(irq->gpio.io); fail1: return ret; } static inline void rfkill_rk_sleep_bt_internal(struct rfkill_rk_data *rfkill, bool sleep) { struct rfkill_rk_gpio *wake = &rfkill->pdata->wake_gpio; DBG("*** bt sleep: %d ***\n", sleep); #ifndef CONFIG_BK3515A_COMBO gpio_direction_output(wake->io, sleep ? !wake->enable : wake->enable); #else if (!sleep) { DBG("HOST_UART0_TX pull down 10us\n"); if (rfkill_rk_setup_gpio(rfkill->pdev, wake, rfkill->pdata->name, "wake") != 0) { return; } gpio_direction_output(wake->io, wake->enable); usleep_range(10, 20); gpio_direction_output(wake->io, !wake->enable); gpio_free(wake->io); } #endif } static void rfkill_rk_delay_sleep_bt(struct work_struct *work) { struct rfkill_rk_data *rfkill = NULL; DBG("Enter %s\n", __func__); rfkill = container_of(work, struct rfkill_rk_data, bt_sleep_delay_work.work); rfkill_rk_sleep_bt_internal(rfkill, BT_SLEEP); } void rfkill_rk_sleep_bt(bool sleep) { struct rfkill_rk_data *rfkill = g_rfkill; struct rfkill_rk_gpio *wake; bool ret; DBG("Enter %s\n", __func__); if (!rfkill) { LOG("*** RFKILL is empty???\n"); return; } wake = &rfkill->pdata->wake_gpio; if (!gpio_is_valid(wake->io)) { DBG("*** Not support bt wakeup and sleep\n"); return; } ret = cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work); rfkill_rk_sleep_bt_internal(rfkill, sleep); #ifdef CONFIG_BT_AUTOSLEEP if (sleep == BT_WAKEUP) { schedule_delayed_work(&rfkill->bt_sleep_delay_work, msecs_to_jiffies(BT_WAKEUP_TIMEOUT)); } #endif } EXPORT_SYMBOL(rfkill_rk_sleep_bt); static int bt_power_state = 0; int rfkill_get_bt_power_state(int *power, bool *toggle) { struct rfkill_rk_data *mrfkill = g_rfkill; if (!mrfkill) { LOG("%s: rfkill-bt driver has not Successful initialized\n", __func__); return -1; } *toggle = mrfkill->pdata->power_toggle; *power = bt_power_state; return 0; } static int rfkill_rk_set_power(void *data, bool blocked) { struct rfkill_rk_data *rfkill = data; struct rfkill_rk_gpio *wake_host = &rfkill->pdata->wake_host_irq.gpio; struct rfkill_rk_gpio *poweron = &rfkill->pdata->poweron_gpio; struct rfkill_rk_gpio *reset = &rfkill->pdata->reset_gpio; struct rfkill_rk_gpio *rts = &rfkill->pdata->rts_gpio; struct pinctrl *pinctrl = rfkill->pdata->pinctrl; int wifi_power = 0; bool toggle = false; DBG("Enter %s\n", __func__); DBG("Set blocked:%d\n", blocked); toggle = rfkill->pdata->power_toggle; DBG("%s: toggle = %s\n", __func__, toggle ? "true" : "false"); if (!blocked) { if (toggle) { rfkill_set_wifi_bt_power(1); msleep(100); } rfkill_rk_sleep_bt(BT_WAKEUP); // ensure bt is wakeup if (gpio_is_valid(wake_host->io)) { LOG("%s: set bt wake_host high!\n", __func__); gpio_direction_output(wake_host->io, 1); msleep(20); } if (gpio_is_valid(poweron->io)) { if (gpio_get_value(poweron->io) == !poweron->enable) { gpio_direction_output(poweron->io, !poweron->enable); msleep(20); gpio_direction_output(poweron->io, poweron->enable); msleep(20); if (gpio_is_valid(wake_host->io)) gpio_direction_input(wake_host->io); } } if (gpio_is_valid(reset->io)) { if (gpio_get_value(reset->io) == !reset->enable) { gpio_direction_output(reset->io, !reset->enable); msleep(20); gpio_direction_output(reset->io, reset->enable); } } if (pinctrl && gpio_is_valid(rts->io)) { pinctrl_select_state(pinctrl, rts->gpio_state); LOG("ENABLE UART_RTS\n"); gpio_direction_output(rts->io, rts->enable); msleep(100); LOG("DISABLE UART_RTS\n"); gpio_direction_output(rts->io, !rts->enable); pinctrl_select_state(pinctrl, rts->default_state); } bt_power_state = 1; LOG("bt turn on power\n"); rfkill_rk_setup_wake_irq(rfkill, 1); } else { if (gpio_is_valid(poweron->io)) { if (gpio_get_value(poweron->io) == poweron->enable) { gpio_direction_output(poweron->io, !poweron->enable); msleep(20); } } bt_power_state = 0; LOG("bt shut off power\n"); if (gpio_is_valid(reset->io)) { if (gpio_get_value(reset->io) == reset->enable) { gpio_direction_output(reset->io, !reset->enable); msleep(20); } } if (toggle) { if (rfkill_get_wifi_power_state(&wifi_power)) { LOG("%s: cannot get wifi power state!\n", __func__); return -EPERM; } if (!wifi_power) { LOG("%s: bt will set vbat to low\n", __func__); rfkill_set_wifi_bt_power(0); } else { LOG("%s: bt shouldn't control the vbat\n", __func__); } } } return 0; } static int rfkill_rk_pm_prepare(struct device *dev) { struct rfkill_rk_data *rfkill = g_rfkill; struct rfkill_rk_gpio *rts; struct rfkill_rk_irq *wake_host_irq; struct pinctrl *pinctrl = rfkill->pdata->pinctrl; DBG("Enter %s\n", __func__); if (!rfkill) return 0; rts = &rfkill->pdata->rts_gpio; wake_host_irq = &rfkill->pdata->wake_host_irq; //To prevent uart to receive bt data when suspended if (pinctrl && gpio_is_valid(rts->io)) { DBG("Disable UART_RTS\n"); pinctrl_select_state(pinctrl, rts->gpio_state); gpio_direction_output(rts->io, !rts->enable); } #ifdef CONFIG_BT_AUTOSLEEP rfkill_rk_sleep_bt(BT_SLEEP); #endif // enable bt wakeup host if (gpio_is_valid(wake_host_irq->gpio.io) && bt_power_state) { DBG("enable irq for bt wakeup host\n"); enable_irq(wake_host_irq->irq); enable_irq_wake(wake_host_irq->irq); } #ifdef CONFIG_RFKILL_RESET rfkill_set_states(rfkill->rfkill_dev, BT_BLOCKED, false); rfkill_rk_set_power(rfkill, BT_BLOCKED); #endif return 0; } static void rfkill_rk_pm_complete(struct device *dev) { struct rfkill_rk_data *rfkill = g_rfkill; struct rfkill_rk_irq *wake_host_irq; struct rfkill_rk_gpio *rts; struct pinctrl *pinctrl = rfkill->pdata->pinctrl; DBG("Enter %s\n", __func__); if (!rfkill) return; wake_host_irq = &rfkill->pdata->wake_host_irq; rts = &rfkill->pdata->rts_gpio; if (gpio_is_valid(wake_host_irq->gpio.io) && bt_power_state) { LOG("** disable irq\n"); disable_irq(wake_host_irq->irq); disable_irq_wake(wake_host_irq->irq); } if (pinctrl && gpio_is_valid(rts->io)) { DBG("Enable UART_RTS\n"); gpio_direction_output(rts->io, rts->enable); pinctrl_select_state(pinctrl, rts->default_state); } } static const struct rfkill_ops rfkill_rk_ops = { .set_block = rfkill_rk_set_power, }; #define PROC_DIR "bluetooth/sleep" static struct proc_dir_entry *bluetooth_dir, *sleep_dir; static ssize_t bluesleep_read_proc_lpm(struct file *file, char __user *buffer, size_t count, loff_t *data) { return sprintf(buffer, "unsupported to read\n"); } static ssize_t bluesleep_write_proc_lpm(struct file *file, const char __user *buffer, size_t count, loff_t *data) { return count; } static ssize_t bluesleep_read_proc_btwrite(struct file *file, char __user *buffer, size_t count, loff_t *data) { return sprintf(buffer, "unsupported to read\n"); } static ssize_t bluesleep_write_proc_btwrite(struct file *file, const char __user *buffer, size_t count, loff_t *data) { char b; if (count < 1) return -EINVAL; if (copy_from_user(&b, buffer, 1)) return -EFAULT; DBG("btwrite %c\n", b); /* HCI_DEV_WRITE */ if (b != '0') rfkill_rk_sleep_bt(BT_WAKEUP); else rfkill_rk_sleep_bt(BT_SLEEP); return count; } #ifdef CONFIG_OF static int bluetooth_platdata_parse_dt(struct device *dev, struct rfkill_rk_platform_data *data) { struct device_node *node = dev->of_node; int gpio; enum of_gpio_flags flags; if (!node) return -ENODEV; memset(data, 0, sizeof(*data)); if (of_find_property(node, "wifi-bt-power-toggle", NULL)) { data->power_toggle = true; LOG("%s: get property wifi-bt-power-toggle.\n", __func__); } else { data->power_toggle = false; } gpio = of_get_named_gpio_flags(node, "uart_rts_gpios", 0, &flags); if (gpio_is_valid(gpio)) { data->rts_gpio.io = gpio; data->rts_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: get property: uart_rts_gpios = %d.\n", __func__, gpio); data->pinctrl = devm_pinctrl_get(dev); if (!IS_ERR(data->pinctrl)) { data->rts_gpio.default_state = pinctrl_lookup_state(data->pinctrl, "default"); data->rts_gpio.gpio_state = pinctrl_lookup_state(data->pinctrl, "rts_gpio"); } else { data->pinctrl = NULL; LOG("%s: dts does't define the uart rts iomux.\n", __func__); return -EINVAL; } } else { data->pinctrl = NULL; data->rts_gpio.io = -EINVAL; LOG("%s: uart_rts_gpios is no-in-use.\n", __func__); } gpio = of_get_named_gpio_flags(node, "BT,power_gpio", 0, &flags); if (gpio_is_valid(gpio)) { data->poweron_gpio.io = gpio; data->poweron_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: get property: BT,power_gpio = %d.\n", __func__, gpio); } else { data->poweron_gpio.io = -1; } gpio = of_get_named_gpio_flags(node, "BT,reset_gpio", 0, &flags); if (gpio_is_valid(gpio)) { data->reset_gpio.io = gpio; data->reset_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: get property: BT,reset_gpio = %d.\n", __func__, gpio); } else { data->reset_gpio.io = -1; } gpio = of_get_named_gpio_flags(node, "BT,wake_gpio", 0, &flags); if (gpio_is_valid(gpio)) { data->wake_gpio.io = gpio; data->wake_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: get property: BT,wake_gpio = %d.\n", __func__, gpio); } else { data->wake_gpio.io = -1; } gpio = of_get_named_gpio_flags(node, "BT,wake_host_irq", 0, &flags); if (gpio_is_valid(gpio)) { data->wake_host_irq.gpio.io = gpio; data->wake_host_irq.gpio.enable = flags; LOG("%s: get property: BT,wake_host_irq = %d.\n", __func__, gpio); } else { data->wake_host_irq.gpio.io = -1; } data->ext_clk = devm_clk_get(dev, "ext_clock"); if (IS_ERR(data->ext_clk)) { LOG("%s: clk_get failed!!!.\n", __func__); } else { clk_prepare_enable(data->ext_clk); } return 0; } #endif //CONFIG_OF static const struct file_operations bluesleep_lpm = { .owner = THIS_MODULE, .read = bluesleep_read_proc_lpm, .write = bluesleep_write_proc_lpm, }; static const struct file_operations bluesleep_btwrite = { .owner = THIS_MODULE, .read = bluesleep_read_proc_btwrite, .write = bluesleep_write_proc_btwrite, }; static int rfkill_rk_probe(struct platform_device *pdev) { struct rfkill_rk_data *rfkill; struct rfkill_rk_platform_data *pdata = pdev->dev.platform_data; int ret = 0; struct proc_dir_entry *ent; DBG("Enter %s\n", __func__); if (!pdata) { #ifdef CONFIG_OF pdata = devm_kzalloc(&pdev->dev, sizeof(struct rfkill_rk_platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; ret = bluetooth_platdata_parse_dt(&pdev->dev, pdata); if (ret < 0) { #endif LOG("%s: No platform data specified\n", __func__); return ret; #ifdef CONFIG_OF } #endif } pdata->name = (char *)bt_name; pdata->type = RFKILL_TYPE_BLUETOOTH; rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL); if (!rfkill) return -ENOMEM; rfkill->pdata = pdata; rfkill->pdev = pdev; g_rfkill = rfkill; bluetooth_dir = proc_mkdir("bluetooth", NULL); if (!bluetooth_dir) { LOG("Unable to create /proc/bluetooth directory"); return -ENOMEM; } sleep_dir = proc_mkdir("sleep", bluetooth_dir); if (!sleep_dir) { LOG("Unable to create /proc/%s directory", PROC_DIR); return -ENOMEM; } /* read/write proc entries */ ent = proc_create("lpm", 0, sleep_dir, &bluesleep_lpm); if (!ent) { LOG("Unable to create /proc/%s/lpm entry", PROC_DIR); ret = -ENOMEM; goto fail_alloc; } /* read/write proc entries */ ent = proc_create("btwrite", 0, sleep_dir, &bluesleep_btwrite); if (!ent) { LOG("Unable to create /proc/%s/btwrite entry", PROC_DIR); ret = -ENOMEM; goto fail_alloc; } DBG("init gpio\n"); ret = rfkill_rk_setup_gpio(pdev, &pdata->poweron_gpio, pdata->name, "poweron"); if (ret) goto fail_gpio; ret = rfkill_rk_setup_gpio(pdev, &pdata->reset_gpio, pdata->name, "reset"); if (ret) goto fail_gpio; ret = rfkill_rk_setup_gpio(pdev, &pdata->wake_gpio, pdata->name, "wake"); if (ret) goto fail_gpio; ret = rfkill_rk_setup_gpio(pdev, &pdata->rts_gpio, rfkill->pdata->name, "rts"); if (ret) goto fail_gpio; wake_lock_init(&rfkill->bt_irq_wl, WAKE_LOCK_SUSPEND, "rfkill_rk_irq_wl"); ret = rfkill_rk_setup_wake_irq(rfkill, 0); if (ret) goto fail_setup_wake_irq; DBG("setup rfkill\n"); rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, &rfkill_rk_ops, rfkill); if (!rfkill->rfkill_dev) goto fail_alloc; rfkill_set_states(rfkill->rfkill_dev, BT_BLOCKED, false); ret = rfkill_register(rfkill->rfkill_dev); if (ret < 0) goto fail_rfkill; INIT_DELAYED_WORK(&rfkill->bt_sleep_delay_work, rfkill_rk_delay_sleep_bt); //rfkill_rk_set_power(rfkill, BT_BLOCKED); // bt turn off power if (gpio_is_valid(pdata->poweron_gpio.io)) { gpio_direction_output(pdata->poweron_gpio.io, !pdata->poweron_gpio.enable); } if (gpio_is_valid(pdata->reset_gpio.io)) { gpio_direction_output(pdata->reset_gpio.io, !pdata->reset_gpio.enable); } platform_set_drvdata(pdev, rfkill); LOG("%s device registered.\n", pdata->name); return 0; fail_rfkill: rfkill_destroy(rfkill->rfkill_dev); fail_alloc: remove_proc_entry("btwrite", sleep_dir); remove_proc_entry("lpm", sleep_dir); fail_setup_wake_irq: wake_lock_destroy(&rfkill->bt_irq_wl); fail_gpio: g_rfkill = NULL; return ret; } static int rfkill_rk_remove(struct platform_device *pdev) { struct rfkill_rk_data *rfkill = platform_get_drvdata(pdev); LOG("Enter %s\n", __func__); rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work); // free gpio if (gpio_is_valid(rfkill->pdata->rts_gpio.io)) gpio_free(rfkill->pdata->rts_gpio.io); if (gpio_is_valid(rfkill->pdata->wake_host_irq.gpio.io)) { free_irq(rfkill->pdata->wake_host_irq.irq, rfkill); #ifndef CONFIG_BK3515A_COMBO gpio_free(rfkill->pdata->wake_host_irq.gpio.io); #endif } #ifndef CONFIG_BK3515A_COMBO if (gpio_is_valid(rfkill->pdata->wake_gpio.io)) gpio_free(rfkill->pdata->wake_gpio.io); #endif if (gpio_is_valid(rfkill->pdata->reset_gpio.io)) gpio_free(rfkill->pdata->reset_gpio.io); if (gpio_is_valid(rfkill->pdata->poweron_gpio.io)) gpio_free(rfkill->pdata->poweron_gpio.io); clk_disable_unprepare(rfkill->pdata->ext_clk); wake_lock_destroy(&rfkill->bt_irq_wl); g_rfkill = NULL; return 0; } static const struct dev_pm_ops rfkill_rk_pm_ops = { .prepare = rfkill_rk_pm_prepare, .complete = rfkill_rk_pm_complete, }; #ifdef CONFIG_OF static struct of_device_id bt_platdata_of_match[] = { { .compatible = "bluetooth-platdata" }, {} }; MODULE_DEVICE_TABLE(of, bt_platdata_of_match); #endif //CONFIG_OF static struct platform_driver rfkill_rk_driver = { .probe = rfkill_rk_probe, .remove = rfkill_rk_remove, .driver = { .name = "rfkill_bt", .owner = THIS_MODULE, .pm = &rfkill_rk_pm_ops, .of_match_table = of_match_ptr(bt_platdata_of_match), }, }; static int __init rfkill_rk_init(void) { int err; LOG("Enter %s\n", __func__); err = rfkill_wlan_init(); if (err) return err; return platform_driver_register(&rfkill_rk_driver); } static void __exit rfkill_rk_exit(void) { LOG("Enter %s\n", __func__); platform_driver_unregister(&rfkill_rk_driver); rfkill_wlan_exit(); } module_init(rfkill_rk_init); module_exit(rfkill_rk_exit); MODULE_DESCRIPTION("rock-chips rfkill for Bluetooth v0.3"); MODULE_AUTHOR("cmy@rock-chips.com, gwl@rock-chips.com"); MODULE_LICENSE("GPL");
(2) linux 6.3
对于linux 6.3版本,需要修改include/linux/of_gpio.h、drivers/gpio/gpiolib-of.c、net/rfkill/rfkill-bt.c、net/rfkill/rfkill-wlan.c文件:
include/linux/of_gpio.h文件添加:
extern int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index, unsigned long *flags);
drivers/gpio/gpiolib-of.c文件添加:
int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index, unsigned long *flags) { struct gpio_desc *desc; enum of_gpio_flags of_flags; desc = of_get_named_gpiod_flags(np, list_name, index, &of_flags); if (IS_ERR(desc)) return PTR_ERR(desc); *flags = of_convert_gpio_flags(of_flags); return desc_to_gpio(desc); }
net/rfkill/rfkill-bt.c修改为:
/* * Copyright (C) 2012 ROCKCHIP, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ /* Rock-chips rfkill driver for bluetooth * */ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/module.h> #include <linux/rfkill.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/rfkill-bt.h> #include <linux/rfkill-wlan.h> #include <linux/wakelock.h> #include <linux/interrupt.h> #include <asm/irq.h> #include <linux/suspend.h> #include <linux/proc_fs.h> #include <linux/uaccess.h> #include <linux/gpio.h> #include <linux/fs.h> #include <dt-bindings/gpio/gpio.h> #include <uapi/linux/rfkill.h> #include <linux/pinctrl/consumer.h> #ifdef CONFIG_OF #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> #endif #if 0 #define DBG(x...) pr_info("[BT_RFKILL]: " x) #else #define DBG(x...) #endif #define LOG(x...) pr_info("[BT_RFKILL]: " x) #define BT_WAKEUP_TIMEOUT 10000 #define BT_IRQ_WAKELOCK_TIMEOUT (10 * 1000) #define BT_BLOCKED true #define BT_UNBLOCK false #define BT_SLEEP true #define BT_WAKEUP false enum { IOMUX_FNORMAL = 0, IOMUX_FGPIO, IOMUX_FMUX, }; struct rfkill_rk_data { struct rfkill_rk_platform_data *pdata; struct platform_device *pdev; struct rfkill *rfkill_dev; struct wake_lock bt_irq_wl; struct delayed_work bt_sleep_delay_work; int irq_req; }; static struct rfkill_rk_data *g_rfkill = NULL; static const char bt_name[] = #if defined(CONFIG_BCM4330) #if defined(CONFIG_BT_MODULE_NH660) "nh660" #else "bcm4330" #endif #elif defined(CONFIG_RK903) #if defined(CONFIG_RKWIFI_26M) "rk903_26M" #else "rk903" #endif #elif defined(CONFIG_BCM4329) "bcm4329" #elif defined(CONFIG_MV8787) "mv8787" #elif defined(CONFIG_AP6210) #if defined(CONFIG_RKWIFI_26M) "ap6210" #else "ap6210_24M" #endif #elif defined(CONFIG_AP6330) "ap6330" #elif defined(CONFIG_AP6476) "ap6476" #elif defined(CONFIG_AP6493) "ap6493" #elif defined(CONFIG_AP6441) "ap6441" #elif defined(CONFIG_AP6335) "ap6335" #elif defined(CONFIG_GB86302I) "gb86302i" #else "bt_default" #endif ; static irqreturn_t rfkill_rk_wake_host_irq(int irq, void *dev) { struct rfkill_rk_data *rfkill = dev; LOG("BT_WAKE_HOST IRQ fired\n"); DBG("BT IRQ wakeup, request %dms wakelock\n", BT_IRQ_WAKELOCK_TIMEOUT); wake_lock_timeout(&rfkill->bt_irq_wl, msecs_to_jiffies(BT_IRQ_WAKELOCK_TIMEOUT)); return IRQ_HANDLED; } static int rfkill_rk_setup_gpio(struct platform_device *pdev, struct rfkill_rk_gpio *gpio, const char *prefix, const char *name) { if (gpio_is_valid(gpio->io)) { int ret = 0; sprintf(gpio->name, "%s_%s", prefix, name); ret = devm_gpio_request(&pdev->dev, gpio->io, gpio->name); if (ret) { LOG("Failed to get %s gpio.\n", gpio->name); return -1; } } return 0; } static int rfkill_rk_setup_wake_irq(struct rfkill_rk_data *rfkill, int flag) { int ret = 0; struct rfkill_rk_irq *irq = &rfkill->pdata->wake_host_irq; if (!flag) { rfkill->irq_req = 0; ret = rfkill_rk_setup_gpio(rfkill->pdev, &irq->gpio, rfkill->pdata->name, "wake_host"); if (ret) goto fail1; } if (gpio_is_valid(irq->gpio.io)) { if (rfkill->irq_req) { rfkill->irq_req = 0; free_irq(irq->irq, rfkill); } LOG("Request irq for bt wakeup host\n"); irq->irq = gpio_to_irq(irq->gpio.io); sprintf(irq->name, "%s_irq", irq->gpio.name); ret = request_irq(irq->irq, rfkill_rk_wake_host_irq, (irq->gpio.enable == GPIO_ACTIVE_LOW) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING, irq->name, rfkill); if (ret) goto fail2; rfkill->irq_req = 1; LOG("** disable irq\n"); disable_irq(irq->irq); /*ret = disable_irq_wake(irq->irq);init irq wake is disabled,no need to disable*/ } return ret; fail2: gpio_free(irq->gpio.io); fail1: return ret; } static inline void rfkill_rk_sleep_bt_internal(struct rfkill_rk_data *rfkill, bool sleep) { struct rfkill_rk_gpio *wake = &rfkill->pdata->wake_gpio; DBG("*** bt sleep: %d ***\n", sleep); #ifndef CONFIG_BK3515A_COMBO gpio_direction_output(wake->io, sleep ? !wake->enable : wake->enable); #else if (!sleep) { DBG("HOST_UART0_TX pull down 10us\n"); if (rfkill_rk_setup_gpio(rfkill->pdev, wake, rfkill->pdata->name, "wake") != 0) { return; } gpio_direction_output(wake->io, wake->enable); usleep_range(10, 20); gpio_direction_output(wake->io, !wake->enable); gpio_free(wake->io); } #endif } static void rfkill_rk_delay_sleep_bt(struct work_struct *work) { struct rfkill_rk_data *rfkill = NULL; DBG("Enter %s\n", __func__); rfkill = container_of(work, struct rfkill_rk_data, bt_sleep_delay_work.work); rfkill_rk_sleep_bt_internal(rfkill, BT_SLEEP); } void rfkill_rk_sleep_bt(bool sleep) { struct rfkill_rk_data *rfkill = g_rfkill; struct rfkill_rk_gpio *wake; bool ret; DBG("Enter %s\n", __func__); if (!rfkill) { LOG("*** RFKILL is empty???\n"); return; } wake = &rfkill->pdata->wake_gpio; if (!gpio_is_valid(wake->io)) { DBG("*** Not support bt wakeup and sleep\n"); return; } ret = cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work); rfkill_rk_sleep_bt_internal(rfkill, sleep); #ifdef CONFIG_BT_AUTOSLEEP if (sleep == BT_WAKEUP) { schedule_delayed_work(&rfkill->bt_sleep_delay_work, msecs_to_jiffies(BT_WAKEUP_TIMEOUT)); } #endif } EXPORT_SYMBOL(rfkill_rk_sleep_bt); static int bt_power_state = 0; int rfkill_get_bt_power_state(int *power, bool *toggle) { struct rfkill_rk_data *mrfkill = g_rfkill; if (!mrfkill) { LOG("%s: rfkill-bt driver has not Successful initialized\n", __func__); return -1; } *toggle = mrfkill->pdata->power_toggle; *power = bt_power_state; return 0; } static int rfkill_rk_set_power(void *data, bool blocked) { struct rfkill_rk_data *rfkill = data; struct rfkill_rk_gpio *wake_host = &rfkill->pdata->wake_host_irq.gpio; struct rfkill_rk_gpio *poweron = &rfkill->pdata->poweron_gpio; struct rfkill_rk_gpio *reset = &rfkill->pdata->reset_gpio; struct rfkill_rk_gpio *rts = &rfkill->pdata->rts_gpio; struct pinctrl *pinctrl = rfkill->pdata->pinctrl; int wifi_power = 0; bool toggle = false; DBG("Enter %s\n", __func__); DBG("Set blocked:%d\n", blocked); toggle = rfkill->pdata->power_toggle; DBG("%s: toggle = %s\n", __func__, toggle ? "true" : "false"); if (!blocked) { if (toggle) { rfkill_set_wifi_bt_power(1); msleep(100); } rfkill_rk_sleep_bt(BT_WAKEUP); // ensure bt is wakeup if (gpio_is_valid(wake_host->io)) { LOG("%s: set bt wake_host high!\n", __func__); gpio_direction_output(wake_host->io, 1); msleep(20); } if (gpio_is_valid(poweron->io)) { if (gpio_get_value(poweron->io) == !poweron->enable) { gpio_direction_output(poweron->io, !poweron->enable); msleep(20); gpio_direction_output(poweron->io, poweron->enable); msleep(20); if (gpio_is_valid(wake_host->io)) gpio_direction_input(wake_host->io); } } if (gpio_is_valid(reset->io)) { if (gpio_get_value(reset->io) == !reset->enable) { gpio_direction_output(reset->io, !reset->enable); msleep(20); gpio_direction_output(reset->io, reset->enable); } } if (pinctrl && gpio_is_valid(rts->io)) { pinctrl_select_state(pinctrl, rts->gpio_state); LOG("ENABLE UART_RTS\n"); gpio_direction_output(rts->io, rts->enable); msleep(100); LOG("DISABLE UART_RTS\n"); gpio_direction_output(rts->io, !rts->enable); pinctrl_select_state(pinctrl, rts->default_state); } bt_power_state = 1; LOG("bt turn on power\n"); rfkill_rk_setup_wake_irq(rfkill, 1); } else { if (gpio_is_valid(poweron->io)) { if (gpio_get_value(poweron->io) == poweron->enable) { gpio_direction_output(poweron->io, !poweron->enable); msleep(20); } } bt_power_state = 0; LOG("bt shut off power\n"); if (gpio_is_valid(reset->io)) { if (gpio_get_value(reset->io) == reset->enable) { gpio_direction_output(reset->io, !reset->enable); msleep(20); } } if (toggle) { if (rfkill_get_wifi_power_state(&wifi_power)) { LOG("%s: cannot get wifi power state!\n", __func__); return -EPERM; } if (!wifi_power) { LOG("%s: bt will set vbat to low\n", __func__); rfkill_set_wifi_bt_power(0); } else { LOG("%s: bt shouldn't control the vbat\n", __func__); } } } return 0; } static int rfkill_rk_pm_prepare(struct device *dev) { struct rfkill_rk_data *rfkill = g_rfkill; struct rfkill_rk_gpio *rts; struct rfkill_rk_irq *wake_host_irq; struct pinctrl *pinctrl = rfkill->pdata->pinctrl; DBG("Enter %s\n", __func__); if (!rfkill) return 0; rts = &rfkill->pdata->rts_gpio; wake_host_irq = &rfkill->pdata->wake_host_irq; //To prevent uart to receive bt data when suspended if (pinctrl && gpio_is_valid(rts->io)) { DBG("Disable UART_RTS\n"); pinctrl_select_state(pinctrl, rts->gpio_state); gpio_direction_output(rts->io, !rts->enable); } #ifdef CONFIG_BT_AUTOSLEEP rfkill_rk_sleep_bt(BT_SLEEP); #endif // enable bt wakeup host if (gpio_is_valid(wake_host_irq->gpio.io) && bt_power_state) { DBG("enable irq for bt wakeup host\n"); enable_irq(wake_host_irq->irq); enable_irq_wake(wake_host_irq->irq); } #ifdef CONFIG_RFKILL_RESET rfkill_set_states(rfkill->rfkill_dev, BT_BLOCKED, false); rfkill_rk_set_power(rfkill, BT_BLOCKED); #endif return 0; } static void rfkill_rk_pm_complete(struct device *dev) { struct rfkill_rk_data *rfkill = g_rfkill; struct rfkill_rk_irq *wake_host_irq; struct rfkill_rk_gpio *rts; struct pinctrl *pinctrl = rfkill->pdata->pinctrl; DBG("Enter %s\n", __func__); if (!rfkill) return; wake_host_irq = &rfkill->pdata->wake_host_irq; rts = &rfkill->pdata->rts_gpio; if (gpio_is_valid(wake_host_irq->gpio.io) && bt_power_state) { LOG("** disable irq\n"); disable_irq(wake_host_irq->irq); disable_irq_wake(wake_host_irq->irq); } if (pinctrl && gpio_is_valid(rts->io)) { DBG("Enable UART_RTS\n"); gpio_direction_output(rts->io, rts->enable); pinctrl_select_state(pinctrl, rts->default_state); } } static const struct rfkill_ops rfkill_rk_ops = { .set_block = rfkill_rk_set_power, }; #define PROC_DIR "bluetooth/sleep" static struct proc_dir_entry *bluetooth_dir, *sleep_dir; static ssize_t bluesleep_read_proc_lpm(struct file *file, char __user *buffer, size_t count, loff_t *data) { return sprintf(buffer, "unsupported to read\n"); } static ssize_t bluesleep_write_proc_lpm(struct file *file, const char __user *buffer, size_t count, loff_t *data) { return count; } static ssize_t bluesleep_read_proc_btwrite(struct file *file, char __user *buffer, size_t count, loff_t *data) { return sprintf(buffer, "unsupported to read\n"); } static ssize_t bluesleep_write_proc_btwrite(struct file *file, const char __user *buffer, size_t count, loff_t *data) { char b; if (count < 1) return -EINVAL; if (copy_from_user(&b, buffer, 1)) return -EFAULT; DBG("btwrite %c\n", b); /* HCI_DEV_WRITE */ if (b != '0') rfkill_rk_sleep_bt(BT_WAKEUP); else rfkill_rk_sleep_bt(BT_SLEEP); return count; } #ifdef CONFIG_OF static int bluetooth_platdata_parse_dt(struct device *dev, struct rfkill_rk_platform_data *data) { struct device_node *node = dev->of_node; int gpio; unsigned long flags; if (!node) return -ENODEV; memset(data, 0, sizeof(*data)); if (of_find_property(node, "wifi-bt-power-toggle", NULL)) { data->power_toggle = true; LOG("%s: get property wifi-bt-power-toggle.\n", __func__); } else { data->power_toggle = false; } gpio = of_get_named_gpio_flags(node, "uart_rts_gpios", 0, &flags); if (gpio_is_valid(gpio)) { data->rts_gpio.io = gpio; data->rts_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: get property: uart_rts_gpios = %d.\n", __func__, gpio); data->pinctrl = devm_pinctrl_get(dev); if (!IS_ERR(data->pinctrl)) { data->rts_gpio.default_state = pinctrl_lookup_state(data->pinctrl, "default"); data->rts_gpio.gpio_state = pinctrl_lookup_state(data->pinctrl, "rts_gpio"); } else { data->pinctrl = NULL; LOG("%s: dts does't define the uart rts iomux.\n", __func__); return -EINVAL; } } else { data->pinctrl = NULL; data->rts_gpio.io = -EINVAL; LOG("%s: uart_rts_gpios is no-in-use.\n", __func__); } gpio = of_get_named_gpio_flags(node, "BT,power_gpio", 0, &flags); if (gpio_is_valid(gpio)) { data->poweron_gpio.io = gpio; data->poweron_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: get property: BT,power_gpio = %d.\n", __func__, gpio); } else { data->poweron_gpio.io = -1; } gpio = of_get_named_gpio_flags(node, "BT,reset_gpio", 0, &flags); if (gpio_is_valid(gpio)) { data->reset_gpio.io = gpio; data->reset_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: get property: BT,reset_gpio = %d.\n", __func__, gpio); } else { data->reset_gpio.io = -1; } gpio = of_get_named_gpio_flags(node, "BT,wake_gpio", 0, &flags); if (gpio_is_valid(gpio)) { data->wake_gpio.io = gpio; data->wake_gpio.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: get property: BT,wake_gpio = %d.\n", __func__, gpio); } else { data->wake_gpio.io = -1; } gpio = of_get_named_gpio_flags(node, "BT,wake_host_irq", 0, &flags); if (gpio_is_valid(gpio)) { data->wake_host_irq.gpio.io = gpio; data->wake_host_irq.gpio.enable = flags; LOG("%s: get property: BT,wake_host_irq = %d.\n", __func__, gpio); } else { data->wake_host_irq.gpio.io = -1; } data->ext_clk = devm_clk_get(dev, "ext_clock"); if (IS_ERR(data->ext_clk)) { LOG("%s: clk_get failed!!!.\n", __func__); } else { clk_prepare_enable(data->ext_clk); } return 0; } #endif //CONFIG_OF static const struct proc_ops bluesleep_lpm = { .proc_read = bluesleep_read_proc_lpm, .proc_write = bluesleep_write_proc_lpm, }; static const struct proc_ops bluesleep_btwrite = { .proc_read = bluesleep_read_proc_btwrite, .proc_write = bluesleep_write_proc_btwrite, }; static int rfkill_rk_probe(struct platform_device *pdev) { struct rfkill_rk_data *rfkill; struct rfkill_rk_platform_data *pdata = pdev->dev.platform_data; int ret = 0; struct proc_dir_entry *ent; DBG("Enter %s\n", __func__); if (!pdata) { #ifdef CONFIG_OF pdata = devm_kzalloc(&pdev->dev, sizeof(struct rfkill_rk_platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; ret = bluetooth_platdata_parse_dt(&pdev->dev, pdata); if (ret < 0) { #endif LOG("%s: No platform data specified\n", __func__); return ret; #ifdef CONFIG_OF } #endif } pdata->name = (char *)bt_name; pdata->type = RFKILL_TYPE_BLUETOOTH; rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL); if (!rfkill) return -ENOMEM; rfkill->pdata = pdata; rfkill->pdev = pdev; g_rfkill = rfkill; bluetooth_dir = proc_mkdir("bluetooth", NULL); if (!bluetooth_dir) { LOG("Unable to create /proc/bluetooth directory"); return -ENOMEM; } sleep_dir = proc_mkdir("sleep", bluetooth_dir); if (!sleep_dir) { LOG("Unable to create /proc/%s directory", PROC_DIR); return -ENOMEM; } /* read/write proc entries */ ent = proc_create("lpm", 0, sleep_dir, &bluesleep_lpm); if (!ent) { LOG("Unable to create /proc/%s/lpm entry", PROC_DIR); ret = -ENOMEM; goto fail_alloc; } /* read/write proc entries */ ent = proc_create("btwrite", 0, sleep_dir, &bluesleep_btwrite); if (!ent) { LOG("Unable to create /proc/%s/btwrite entry", PROC_DIR); ret = -ENOMEM; goto fail_alloc; } DBG("init gpio\n"); ret = rfkill_rk_setup_gpio(pdev, &pdata->poweron_gpio, pdata->name, "poweron"); if (ret) goto fail_gpio; ret = rfkill_rk_setup_gpio(pdev, &pdata->reset_gpio, pdata->name, "reset"); if (ret) goto fail_gpio; ret = rfkill_rk_setup_gpio(pdev, &pdata->wake_gpio, pdata->name, "wake"); if (ret) goto fail_gpio; ret = rfkill_rk_setup_gpio(pdev, &pdata->rts_gpio, rfkill->pdata->name, "rts"); if (ret) goto fail_gpio; wake_lock_init(&rfkill->bt_irq_wl, WAKE_LOCK_SUSPEND, "rfkill_rk_irq_wl"); ret = rfkill_rk_setup_wake_irq(rfkill, 0); if (ret) goto fail_setup_wake_irq; DBG("setup rfkill\n"); rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, &rfkill_rk_ops, rfkill); if (!rfkill->rfkill_dev) goto fail_alloc; rfkill_set_states(rfkill->rfkill_dev, BT_BLOCKED, false); ret = rfkill_register(rfkill->rfkill_dev); if (ret < 0) goto fail_rfkill; INIT_DELAYED_WORK(&rfkill->bt_sleep_delay_work, rfkill_rk_delay_sleep_bt); //rfkill_rk_set_power(rfkill, BT_BLOCKED); // bt turn off power if (gpio_is_valid(pdata->poweron_gpio.io)) { gpio_direction_output(pdata->poweron_gpio.io, !pdata->poweron_gpio.enable); } if (gpio_is_valid(pdata->reset_gpio.io)) { gpio_direction_output(pdata->reset_gpio.io, !pdata->reset_gpio.enable); } platform_set_drvdata(pdev, rfkill); LOG("%s device registered.\n", pdata->name); return 0; fail_rfkill: rfkill_destroy(rfkill->rfkill_dev); fail_alloc: remove_proc_entry("btwrite", sleep_dir); remove_proc_entry("lpm", sleep_dir); fail_setup_wake_irq: wake_lock_destroy(&rfkill->bt_irq_wl); fail_gpio: g_rfkill = NULL; return ret; } static int rfkill_rk_remove(struct platform_device *pdev) { struct rfkill_rk_data *rfkill = platform_get_drvdata(pdev); LOG("Enter %s\n", __func__); rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); cancel_delayed_work_sync(&rfkill->bt_sleep_delay_work); // free gpio if (gpio_is_valid(rfkill->pdata->rts_gpio.io)) gpio_free(rfkill->pdata->rts_gpio.io); if (gpio_is_valid(rfkill->pdata->wake_host_irq.gpio.io)) { free_irq(rfkill->pdata->wake_host_irq.irq, rfkill); #ifndef CONFIG_BK3515A_COMBO gpio_free(rfkill->pdata->wake_host_irq.gpio.io); #endif } #ifndef CONFIG_BK3515A_COMBO if (gpio_is_valid(rfkill->pdata->wake_gpio.io)) gpio_free(rfkill->pdata->wake_gpio.io); #endif if (gpio_is_valid(rfkill->pdata->reset_gpio.io)) gpio_free(rfkill->pdata->reset_gpio.io); if (gpio_is_valid(rfkill->pdata->poweron_gpio.io)) gpio_free(rfkill->pdata->poweron_gpio.io); clk_disable_unprepare(rfkill->pdata->ext_clk); wake_lock_destroy(&rfkill->bt_irq_wl); g_rfkill = NULL; return 0; } static const struct dev_pm_ops rfkill_rk_pm_ops = { .prepare = rfkill_rk_pm_prepare, .complete = rfkill_rk_pm_complete, }; #ifdef CONFIG_OF static struct of_device_id bt_platdata_of_match[] = { { .compatible = "bluetooth-platdata" }, {} }; MODULE_DEVICE_TABLE(of, bt_platdata_of_match); #endif //CONFIG_OF static struct platform_driver rfkill_rk_driver = { .probe = rfkill_rk_probe, .remove = rfkill_rk_remove, .driver = { .name = "rfkill_bt", .owner = THIS_MODULE, .pm = &rfkill_rk_pm_ops, .of_match_table = of_match_ptr(bt_platdata_of_match), }, }; static int __init rfkill_rk_init(void) { int err; LOG("Enter %s\n", __func__); err = rfkill_wlan_init(); if (err) return err; return platform_driver_register(&rfkill_rk_driver); } static void __exit rfkill_rk_exit(void) { LOG("Enter %s\n", __func__); platform_driver_unregister(&rfkill_rk_driver); rfkill_wlan_exit(); } module_init(rfkill_rk_init); module_exit(rfkill_rk_exit); MODULE_DESCRIPTION("rock-chips rfkill for Bluetooth v0.3"); MODULE_AUTHOR("cmy@rock-chips.com, gwl@rock-chips.com"); MODULE_LICENSE("GPL");
net/rfkill/rfkill-wlan.c修改为:
/* * Copyright (C) 2012 ROCKCHIP, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ /* Rock-chips rfkill driver for wifi */ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/module.h> #include <linux/rfkill.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> #include <linux/delay.h> #include <linux/rfkill-wlan.h> #include <linux/rfkill-bt.h> #include <linux/wakelock.h> #include <linux/interrupt.h> #include <asm/irq.h> #include <linux/suspend.h> #include <linux/proc_fs.h> #include <linux/uaccess.h> #include <linux/gpio.h> #include <dt-bindings/gpio/gpio.h> #include <linux/skbuff.h> #include <linux/fb.h> #include <linux/rockchip/grf.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <linux/mmc/host.h> #ifdef CONFIG_OF #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> #endif #include <linux/soc/rockchip/rk_vendor_storage.h> #include <linux/device.h> #include "../../drivers/mmc/core/pwrseq.h" #if 0 #define DBG(x...) pr_info("[WLAN_RFKILL]: " x) #else #define DBG(x...) #endif #define LOG(x...) pr_info("[WLAN_RFKILL]: " x) struct rfkill_wlan_data { struct rksdmmc_gpio_wifi_moudle *pdata; struct wake_lock wlan_irq_wl; }; static struct rfkill_wlan_data *g_rfkill = NULL; static int power_set_time = 0; static int wifi_bt_vbat_state; static int wifi_power_state; static const char wlan_name[] = "rkwifi"; static char wifi_chip_type_string[64]; /*********************************************************** * * Broadcom Wifi Static Memory * **********************************************************/ #ifdef CONFIG_RKWIFI #define BCM_STATIC_MEMORY_SUPPORT 0 #else #define BCM_STATIC_MEMORY_SUPPORT 0 #endif //=========================== #if BCM_STATIC_MEMORY_SUPPORT #define PREALLOC_WLAN_SEC_NUM 4 #define PREALLOC_WLAN_BUF_NUM 160 #define PREALLOC_WLAN_SECTION_HEADER 0 #define WLAN_SKB_BUF_NUM 16 #define WLAN_SECTION_SIZE_0 (12 * 1024) #define WLAN_SECTION_SIZE_1 (12 * 1024) #define WLAN_SECTION_SIZE_2 (32 * 1024) #define WLAN_SECTION_SIZE_3 (136 * 1024) #define WLAN_SECTION_SIZE_4 (4 * 1024) #define WLAN_SECTION_SIZE_5 (64 * 1024) #define WLAN_SECTION_SIZE_6 (4 * 1024) #define WLAN_SECTION_SIZE_7 (4 * 1024) static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM + 1]; struct wifi_mem_prealloc { void *mem_ptr; unsigned long size; }; static struct wifi_mem_prealloc wifi_mem_array[8] = { { NULL, (WLAN_SECTION_SIZE_0) }, { NULL, (WLAN_SECTION_SIZE_1) }, { NULL, (WLAN_SECTION_SIZE_2) }, { NULL, (WLAN_SECTION_SIZE_3) }, { NULL, (WLAN_SECTION_SIZE_4) }, { NULL, (WLAN_SECTION_SIZE_5) }, { NULL, (WLAN_SECTION_SIZE_6) }, { NULL, (WLAN_SECTION_SIZE_7) } }; static int rockchip_init_wifi_mem(void) { int i; int j; for (i = 0; i < WLAN_SKB_BUF_NUM; i++) { wlan_static_skb[i] = dev_alloc_skb(((i < (WLAN_SKB_BUF_NUM / 2)) ? (PAGE_SIZE * 1) : (PAGE_SIZE * 2))); if (!wlan_static_skb[i]) goto err_skb_alloc; } wlan_static_skb[i] = dev_alloc_skb((PAGE_SIZE * 4)); if (!wlan_static_skb[i]) goto err_skb_alloc; for (i = 0; i <= 7; i++) { wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size, GFP_KERNEL); if (!wifi_mem_array[i].mem_ptr) goto err_mem_alloc; } return 0; err_mem_alloc: pr_err("Failed to mem_alloc for WLAN\n"); for (j = 0; j < i; j++) kfree(wifi_mem_array[j].mem_ptr); i = WLAN_SKB_BUF_NUM; err_skb_alloc: pr_err("Failed to skb_alloc for WLAN\n"); for (j = 0; j < i; j++) dev_kfree_skb(wlan_static_skb[j]); dev_kfree_skb(wlan_static_skb[j]); return -ENOMEM; } void *rockchip_mem_prealloc(int section, unsigned long size) { if (section == PREALLOC_WLAN_SEC_NUM) return wlan_static_skb; if (section < 0 || section > 7) return NULL; if (wifi_mem_array[section].size < size) return NULL; return wifi_mem_array[section].mem_ptr; } #else void *rockchip_mem_prealloc(int section, unsigned long size) { return NULL; } #endif EXPORT_SYMBOL(rockchip_mem_prealloc); int rfkill_set_wifi_bt_power(int on) { struct rfkill_wlan_data *mrfkill = g_rfkill; struct rksdmmc_gpio *vbat; LOG("%s: %d\n", __func__, on); if (!mrfkill) { LOG("%s: rfkill-wlan driver has not Successful initialized\n", __func__); return -1; } vbat = &mrfkill->pdata->vbat_n; if (on) { if (gpio_is_valid(vbat->io)) gpio_direction_output(vbat->io, vbat->enable); } else { if (gpio_is_valid(vbat->io)) gpio_direction_output(vbat->io, !(vbat->enable)); } wifi_bt_vbat_state = on; return 0; } /************************************************************************** * * get wifi power state Func * *************************************************************************/ int rfkill_get_wifi_power_state(int *power) { struct rfkill_wlan_data *mrfkill = g_rfkill; if (!mrfkill) { LOG("%s: rfkill-wlan driver has not Successful initialized\n", __func__); return -1; } *power = wifi_power_state; return 0; } EXPORT_SYMBOL(rfkill_get_wifi_power_state); /************************************************************************** * * Wifi Power Control Func * 0 -> power off * 1 -> power on * *************************************************************************/ int rockchip_wifi_power(int on) { struct rfkill_wlan_data *mrfkill = g_rfkill; struct rksdmmc_gpio *poweron, *reset; struct regulator *ldo = NULL; int bt_power = 0; bool toggle = false; LOG("%s: %d\n", __func__, on); if (!mrfkill) { LOG("%s: rfkill-wlan driver has not Successful initialized\n", __func__); return -1; } if (mrfkill->pdata->wifi_power_remain && power_set_time) { LOG("%s: wifi power is setted to be remain on.", __func__); return 0; } power_set_time++; if (!rfkill_get_bt_power_state(&bt_power, &toggle)) { LOG("%s: toggle = %s\n", __func__, toggle ? "true" : "false"); } if (mrfkill->pdata->mregulator.power_ctrl_by_pmu) { int ret = -1; char *ldostr; int level = mrfkill->pdata->mregulator.enable; ldostr = mrfkill->pdata->mregulator.pmu_regulator; if (!ldostr) return -1; ldo = regulator_get(NULL, ldostr); if (!ldo || IS_ERR(ldo)) { LOG("\n\n\n%s get ldo error,please mod this\n\n\n", __func__); return -1; } if (on == level) { regulator_set_voltage(ldo, 3000000, 3000000); LOG("%s: %s enabled\n", __func__, ldostr); ret = regulator_enable(ldo); if (ret) LOG("ldo enable failed\n"); wifi_power_state = 1; LOG("wifi turn on power.\n"); } else { LOG("%s: %s disabled\n", __func__, ldostr); while (regulator_is_enabled(ldo) > 0) { ret = regulator_disable(ldo); if (ret) LOG("ldo disable failed\n"); } wifi_power_state = 0; LOG("wifi shut off power.\n"); } regulator_put(ldo); msleep(100); } else { poweron = &mrfkill->pdata->power_n; reset = &mrfkill->pdata->reset_n; if (on) { if (toggle) { rfkill_set_wifi_bt_power(1); msleep(100); } if (gpio_is_valid(poweron->io)) { gpio_direction_output(poweron->io, poweron->enable); msleep(100); } if (gpio_is_valid(reset->io)) { gpio_direction_output(reset->io, reset->enable); msleep(100); } wifi_power_state = 1; LOG("wifi turn on power [GPIO%d-%d]\n", poweron->io, poweron->enable); } else { if (gpio_is_valid(poweron->io)) { printk("wifi power off\n"); gpio_direction_output(poweron->io, !(poweron->enable)); msleep(100); } if (gpio_is_valid(reset->io)) { gpio_direction_output(reset->io, !(reset->enable)); } wifi_power_state = 0; if (toggle) { if (!bt_power) { LOG("%s: wifi will set vbat to low\n", __func__); rfkill_set_wifi_bt_power(0); } else { LOG("%s: wifi shouldn't control the vbat\n", __func__); } } LOG("wifi shut off power [GPIO%d-%d]\n", poweron->io, !poweron->enable); } } return 0; } EXPORT_SYMBOL(rockchip_wifi_power); /************************************************************************** * * Wifi Sdio Detect Func * *************************************************************************/ int rockchip_wifi_set_carddetect(int val) { return 0; } EXPORT_SYMBOL(rockchip_wifi_set_carddetect); /************************************************************************** * * Wifi Get Interrupt irq Func * *************************************************************************/ int rockchip_wifi_get_oob_irq(void) { struct rfkill_wlan_data *mrfkill = g_rfkill; struct rksdmmc_gpio *wifi_int_irq; LOG("%s: Enter\n", __func__); if (!mrfkill) { LOG("%s: rfkill-wlan driver has not Successful initialized\n", __func__); return -1; } wifi_int_irq = &mrfkill->pdata->wifi_int_b; if (gpio_is_valid(wifi_int_irq->io)) { return gpio_to_irq(wifi_int_irq->io); //return wifi_int_irq->io; } else { LOG("%s: wifi OOB pin isn't defined.\n", __func__); } return -1; } EXPORT_SYMBOL(rockchip_wifi_get_oob_irq); int rockchip_wifi_get_oob_irq_flag(void) { struct rfkill_wlan_data *mrfkill = g_rfkill; struct rksdmmc_gpio *wifi_int_irq; int gpio_flags = -1; if (mrfkill) { wifi_int_irq = &mrfkill->pdata->wifi_int_b; if (gpio_is_valid(wifi_int_irq->io)) gpio_flags = wifi_int_irq->enable; } return gpio_flags; } EXPORT_SYMBOL(rockchip_wifi_get_oob_irq_flag); /************************************************************************** * * Wifi Reset Func * *************************************************************************/ int rockchip_wifi_reset(int on) { return 0; } EXPORT_SYMBOL(rockchip_wifi_reset); /************************************************************************** * * Wifi MAC custom Func * *************************************************************************/ #include <linux/etherdevice.h> #include <linux/errno.h> u8 wifi_custom_mac_addr[6] = { 0, 0, 0, 0, 0, 0 }; //#define RANDOM_ADDRESS_SAVE static int get_wifi_addr_vendor(unsigned char *addr) { int ret; int count = 5; while (count-- > 0) { if (is_rk_vendor_ready()) break; /* sleep 500ms wait rk vendor driver ready */ msleep(500); } ret = rk_vendor_read(WIFI_MAC_ID, addr, 6); if (ret != 6 || is_zero_ether_addr(addr)) { LOG("%s: rk_vendor_read wifi mac address failed (%d)\n", __func__, ret); #ifdef CONFIG_WIFI_GENERATE_RANDOM_MAC_ADDR random_ether_addr(addr); LOG("%s: generate random wifi mac address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", __func__, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); ret = rk_vendor_write(WIFI_MAC_ID, addr, 6); if (ret != 0) { LOG("%s: rk_vendor_write failed %d\n" __func__, ret); memset(addr, 0, 6); return -1; } #else return -1; #endif } else { LOG("%s: rk_vendor_read wifi mac address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", __func__, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } return 0; } int rockchip_wifi_mac_addr(unsigned char *buf) { char mac_buf[20] = { 0 }; LOG("%s: enter.\n", __func__); // from vendor storage if (is_zero_ether_addr(wifi_custom_mac_addr)) { if (get_wifi_addr_vendor(wifi_custom_mac_addr) != 0) return -1; } sprintf(mac_buf, "%02x:%02x:%02x:%02x:%02x:%02x", wifi_custom_mac_addr[0], wifi_custom_mac_addr[1], wifi_custom_mac_addr[2], wifi_custom_mac_addr[3], wifi_custom_mac_addr[4], wifi_custom_mac_addr[5]); LOG("falsh wifi_custom_mac_addr=[%s]\n", mac_buf); if (is_valid_ether_addr(wifi_custom_mac_addr)) { if (!strncmp(wifi_chip_type_string, "rtl", 3)) wifi_custom_mac_addr[0] &= ~0x2; // for p2p } else { LOG("This mac address is not valid, ignored...\n"); return -1; } memcpy(buf, wifi_custom_mac_addr, 6); return 0; } EXPORT_SYMBOL(rockchip_wifi_mac_addr); /************************************************************************** * * wifi get country code func * *************************************************************************/ struct cntry_locales_custom { char iso_abbrev[4]; /* ISO 3166-1 country abbreviation */ char custom_locale[4]; /* Custom firmware locale */ int custom_locale_rev; /* Custom local revisin default -1 */ }; static struct cntry_locales_custom country_cloc; void *rockchip_wifi_country_code(char *ccode) { struct cntry_locales_custom *mcloc; LOG("%s: set country code [%s]\n", __func__, ccode); mcloc = &country_cloc; memcpy(mcloc->custom_locale, ccode, 4); mcloc->custom_locale_rev = 0; return mcloc; } EXPORT_SYMBOL(rockchip_wifi_country_code); /**************************************************************************/ static int rfkill_rk_setup_gpio(struct rksdmmc_gpio *gpio, const char *prefix, const char *name) { if (gpio_is_valid(gpio->io)) { int ret = 0; sprintf(gpio->name, "%s_%s", prefix, name); ret = gpio_request(gpio->io, gpio->name); if (ret) { LOG("Failed to get %s gpio.\n", gpio->name); return -1; } } return 0; } #ifdef CONFIG_OF static int wlan_platdata_parse_dt(struct device *dev, struct rksdmmc_gpio_wifi_moudle *data) { struct device_node *node = dev->of_node; const char *strings; u32 value; int gpio, ret; unsigned long flags; u32 ext_clk_value = 0; if (!node) return -ENODEV; memset(data, 0, sizeof(*data)); #ifdef CONFIG_MFD_SYSCON data->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf"); if (IS_ERR(data->grf)) { LOG("can't find rockchip,grf property\n"); //return -1; } #endif ret = of_property_read_string(node, "wifi_chip_type", &strings); if (ret) { LOG("%s: Can not read wifi_chip_type, set default to rkwifi.\n", __func__); strcpy(wifi_chip_type_string, "rkwifi"); } else { if (strings && strlen(strings) < 64) strcpy(wifi_chip_type_string, strings); } LOG("%s: wifi_chip_type = %s\n", __func__, wifi_chip_type_string); if (of_find_property(node, "keep_wifi_power_on", NULL)) { data->wifi_power_remain = true; LOG("%s: wifi power remain\n", __func__); } else { data->wifi_power_remain = false; LOG("%s: enable wifi power control.\n", __func__); } if (of_find_property(node, "power_ctrl_by_pmu", NULL)) { data->mregulator.power_ctrl_by_pmu = true; ret = of_property_read_string(node, "power_pmu_regulator", &strings); if (ret) { LOG("%s: Can not read property: power_pmu_regulator.\n", __func__); data->mregulator.power_ctrl_by_pmu = false; } else { LOG("%s: wifi power controlled by pmu(%s).\n", __func__, strings); sprintf(data->mregulator.pmu_regulator, "%s", strings); } ret = of_property_read_u32(node, "power_pmu_enable_level", &value); if (ret) { LOG("%s: Can not read: power_pmu_enable_level.\n", __func__); data->mregulator.power_ctrl_by_pmu = false; } else { LOG("%s: wifi power controlled by pmu(level = %s).\n", __func__, (value == 1) ? "HIGH" : "LOW"); data->mregulator.enable = value; } } else { data->mregulator.power_ctrl_by_pmu = false; LOG("%s: wifi power controled by gpio.\n", __func__); gpio = of_get_named_gpio_flags(node, "WIFI,poweren_gpio", 0, &flags); if (gpio_is_valid(gpio)) { data->power_n.io = gpio; data->power_n.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: WIFI,poweren_gpio = %d flags = %d.\n", __func__, gpio, flags); } else { data->power_n.io = -1; } gpio = of_get_named_gpio_flags(node, "WIFI,vbat_gpio", 0, &flags); if (gpio_is_valid(gpio)) { data->vbat_n.io = gpio; data->vbat_n.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: WIFI,vbat_gpio = %d, flags = %d.\n", __func__, gpio, flags); } else { data->vbat_n.io = -1; } gpio = of_get_named_gpio_flags(node, "WIFI,reset_gpio", 0, &flags); if (gpio_is_valid(gpio)) { data->reset_n.io = gpio; data->reset_n.enable = (flags == GPIO_ACTIVE_HIGH) ? 1 : 0; LOG("%s: WIFI,reset_gpio = %d, flags = %d.\n", __func__, gpio, flags); } else { data->reset_n.io = -1; } gpio = of_get_named_gpio_flags(node, "WIFI,host_wake_irq", 0, &flags); if (gpio_is_valid(gpio)) { data->wifi_int_b.io = gpio; data->wifi_int_b.enable = !flags; LOG("%s: WIFI,host_wake_irq = %d, flags = %d.\n", __func__, gpio, flags); } else { data->wifi_int_b.io = -1; } } data->ext_clk = devm_clk_get(dev, "clk_wifi"); if (IS_ERR(data->ext_clk)) { LOG("%s: The ref_wifi_clk not found !\n", __func__); } else { of_property_read_u32(node, "ref-clock-frequency", &ext_clk_value); if (ext_clk_value > 0) { ret = clk_set_rate(data->ext_clk, ext_clk_value); if (ret) LOG("%s: set ref clk error!\n", __func__); } ret = clk_prepare_enable(data->ext_clk); if (ret) LOG("%s: enable ref clk error!\n", __func__); /* WIFI clock (REF_CLKOUT) output enable. * 1'b0: drive disable * 1'b1: output enable */ if (of_machine_is_compatible("rockchip,rk3308")) regmap_write(data->grf, 0x0314, 0x00020002); } return 0; } #endif //CONFIG_OF #if defined(CONFIG_HAS_EARLYSUSPEND) #include <linux/earlysuspend.h> static void wlan_early_suspend(struct early_suspend *h) { LOG("%s :enter\n", __func__); return; } static void wlan_late_resume(struct early_suspend *h) { LOG("%s :enter\n", __func__); return; } struct early_suspend wlan_early_suspend { .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN - 20; .suspend = wlan_early_suspend; .resume = wlan_late_resume; } #endif static void rfkill_wlan_early_suspend(void) { //LOG("%s :enter\n", __func__); return; } static void rfkill_wlan_later_resume(void) { //LOG("%s :enter\n", __func__); return; } static int rfkill_wlan_fb_event_notify(struct notifier_block *self, unsigned long action, void *data) { struct fb_event *event = data; int blank_mode = *((int *)event->data); switch (blank_mode) { case FB_BLANK_UNBLANK: rfkill_wlan_later_resume(); break; case FB_BLANK_NORMAL: rfkill_wlan_early_suspend(); break; default: rfkill_wlan_early_suspend(); break; } return 0; } static struct notifier_block rfkill_wlan_fb_notifier = { .notifier_call = rfkill_wlan_fb_event_notify, }; static ssize_t wifi_power_show(struct class *cls, struct class_attribute *attr, char *_buf) { return sprintf(_buf, "%d\n", wifi_power_state); } static ssize_t wifi_power_store(struct class *cls, struct class_attribute *attr, const char *_buf, size_t _count) { long poweren = 0; if (kstrtol(_buf, 10, &poweren) < 0) return -EINVAL; LOG("%s: poweren = %ld\n", __func__, poweren); if (poweren > 0) rockchip_wifi_power(1); else rockchip_wifi_power(0); return _count; } static CLASS_ATTR_RW(wifi_power); static ssize_t wifi_bt_vbat_show(struct class *cls, struct class_attribute *attr, char *_buf) { return sprintf(_buf, "%d\n", wifi_bt_vbat_state); } static ssize_t wifi_bt_vbat_store(struct class *cls, struct class_attribute *attr, const char *_buf, size_t _count) { long vbat = 0; if (kstrtol(_buf, 10, &vbat) < 0) return -EINVAL; LOG("%s: vbat = %ld\n", __func__, vbat); if (vbat > 0) rfkill_set_wifi_bt_power(1); else rfkill_set_wifi_bt_power(0); return _count; } static CLASS_ATTR_RW(wifi_bt_vbat); static ssize_t wifi_set_carddetect_store(struct class *cls, struct class_attribute *attr, const char *_buf, size_t _count) { long val = 0; if (kstrtol(_buf, 10, &val) < 0) return -EINVAL; LOG("%s: val = %ld\n", __func__, val); if (val > 0) rockchip_wifi_set_carddetect(1); else rockchip_wifi_set_carddetect(0); return _count; } static CLASS_ATTR_WO(wifi_set_carddetect); static struct attribute *rkwifi_power_attrs[] = { &class_attr_wifi_power.attr, &class_attr_wifi_bt_vbat.attr, &class_attr_wifi_set_carddetect.attr, NULL, }; ATTRIBUTE_GROUPS(rkwifi_power); /** Device model classes */ static struct class rkwifi_power = { .name = "rkwifi", .class_groups = rkwifi_power_groups, }; static int rfkill_wlan_probe(struct platform_device *pdev) { struct rfkill_wlan_data *rfkill; struct rksdmmc_gpio_wifi_moudle *pdata = pdev->dev.platform_data; int ret = -1; LOG("Enter %s\n", __func__); class_register(&rkwifi_power); if (!pdata) { #ifdef CONFIG_OF pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; ret = wlan_platdata_parse_dt(&pdev->dev, pdata); if (ret < 0) { #endif LOG("%s: No platform data specified\n", __func__); return ret; #ifdef CONFIG_OF } #endif } rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL); if (!rfkill) goto rfkill_alloc_fail; rfkill->pdata = pdata; g_rfkill = rfkill; LOG("%s: init gpio\n", __func__); if (!pdata->mregulator.power_ctrl_by_pmu) { ret = rfkill_rk_setup_gpio(&pdata->vbat_n, wlan_name, "wlan_vbat"); if (ret) goto fail_alloc; ret = rfkill_rk_setup_gpio(&pdata->reset_n, wlan_name, "wlan_reset"); if (ret) goto fail_alloc; } wake_lock_init(&rfkill->wlan_irq_wl, WAKE_LOCK_SUSPEND, "rfkill_wlan_wake"); rfkill_set_wifi_bt_power(1); #ifdef CONFIG_SDIO_KEEPALIVE if (gpio_is_valid(pdata->power_n.io) && gpio_direction_output(pdata->power_n.io, pdata->power_n.enable); #endif if (pdata->wifi_power_remain) rockchip_wifi_power(1); #if BCM_STATIC_MEMORY_SUPPORT rockchip_init_wifi_mem(); #endif #if defined(CONFIG_HAS_EARLYSUSPEND) register_early_suspend(wlan_early_suspend); #endif fb_register_client(&rfkill_wlan_fb_notifier); LOG("Exit %s\n", __func__); return 0; fail_alloc: kfree(rfkill); rfkill_alloc_fail: kfree(pdata); g_rfkill = NULL; return ret; } static int rfkill_wlan_remove(struct platform_device *pdev) { struct rfkill_wlan_data *rfkill = platform_get_drvdata(pdev); LOG("Enter %s\n", __func__); wake_lock_destroy(&rfkill->wlan_irq_wl); fb_unregister_client(&rfkill_wlan_fb_notifier); if (gpio_is_valid(rfkill->pdata->power_n.io)) gpio_free(rfkill->pdata->power_n.io); if (gpio_is_valid(rfkill->pdata->reset_n.io)) gpio_free(rfkill->pdata->reset_n.io); kfree(rfkill); g_rfkill = NULL; return 0; } static void rfkill_wlan_shutdown(struct platform_device *pdev) { LOG("Enter %s\n", __func__); rockchip_wifi_power(0); rfkill_set_wifi_bt_power(0); } static int rfkill_wlan_suspend(struct platform_device *pdev, pm_message_t state) { LOG("Enter %s\n", __func__); return 0; } static int rfkill_wlan_resume(struct platform_device *pdev) { LOG("Enter %s\n", __func__); return 0; } #ifdef CONFIG_OF static struct of_device_id wlan_platdata_of_match[] = { { .compatible = "wlan-platdata" }, {} }; MODULE_DEVICE_TABLE(of, wlan_platdata_of_match); #endif //CONFIG_OF static struct platform_driver rfkill_wlan_driver = { .probe = rfkill_wlan_probe, .remove = rfkill_wlan_remove, .shutdown = rfkill_wlan_shutdown, .suspend = rfkill_wlan_suspend, .resume = rfkill_wlan_resume, .driver = { .name = "wlan-platdata", .owner = THIS_MODULE, .of_match_table = of_match_ptr(wlan_platdata_of_match), }, }; int __init rfkill_wlan_init(void) { LOG("Enter %s\n", __func__); return platform_driver_register(&rfkill_wlan_driver); } void __exit rfkill_wlan_exit(void) { LOG("Enter %s\n", __func__); platform_driver_unregister(&rfkill_wlan_driver); } MODULE_DESCRIPTION("rock-chips rfkill for wifi v0.1"); MODULE_AUTHOR("gwl@rock-chips.com"); MODULE_LICENSE("GPL");
此外,无论针对linux 5.28还是linux 6.3,均需要修改net/rfkill/Makefile添加如下代码:
rfkill-rk-y += rfkill-wlan.o rfkill-bt.o
obj-$(CONFIG_RFKILL_RK) += rfkill-rk.o
修改net/rfkill/Kconfig添加如下代码:
config RFKILL_RK tristate "Rockchip RFKILL driver" depends on RFKILL depends on MMC depends on ARCH_ROCKCHIP default n help Rockchip rfkill driver for rk29/rk3X
4.3.2 新增wireless-wlan设备节点
为了支持rfkill WiFi驱动,在arch/arm64/boot/dts/rockchip/rk3399-evb.dts根节点下新增:
wireless-wlan { compatible = "wlan-platdata"; rockchip,grf = <&grf>; wifi_chip_type = "ap6356"; sdio_vref = <1800>; WIFI,host_wake_irq = <&gpio0 3 GPIO_ACTIVE_HIGH>; /* GPIO0_a3 */ status = "okay"; };
这里比较重要的属性:
- wifi_chip_type:指定WiFi芯片型号;
- WIFI,host_wake_irq:指定WiFi芯片唤醒主机的中断引脚,这里配置的位RK399 GPIO0_A3引脚;
4.3.3 新增wireless-bluetooth设备节点
为了支持rfkill蓝牙驱动,我们需要在arch/arm64/boot/dts/rockchip/rk3399-evb.dts根节点下新增:
wireless-bluetooth { compatible = "bluetooth-platdata"; clocks = <&rk808 1>; clock-names = "ext_clock"; //wifi-bt-power-toggle; uart_rts_gpios = <&gpio2 19 GPIO_ACTIVE_LOW>; /* GPIO2_C3 */ pinctrl-names = "default", "rts_gpio"; pinctrl-0 = <&uart0_rts>; pinctrl-1 = <&uart0_gpios>; //BT,power_gpio = <&gpio3 19 GPIO_ACTIVE_HIGH>; /* GPIOx_xx */ BT,reset_gpio = <&gpio0 9 GPIO_ACTIVE_HIGH>; /* GPIO0_B1 */ BT,wake_gpio = <&gpio2 26 GPIO_ACTIVE_HIGH>; /* GPIO2_D2 */ BT,wake_host_irq = <&gpio0 4 GPIO_ACTIVE_HIGH>; /* GPIO0_A4 */ status = "okay"; };
其中:
- clocks:指定了设备使用的时钟源,即使用rk808时钟控制器ID为1的时钟,rk808是一个时钟提供者clock provider;
- clock-names:指定了所使用的时钟的名称,即 "ext_clock";
- uart_rts_gpios:表示控制蓝牙串口请求发送引脚UART_CTS_N为GPIO2_C3,低电平有效;
- pinctrl-names:表示该设备节点支持两种pinctrl模式,分别为default和rts_gpio;
- pinctrl-0:设置default状态对应的引脚配置为uart0_rts;uart0_rts定义GPIO2_C3功能复用为UART RST;
- pinctrl-1:表设置rts_gpio状态对应的引脚配置为uart0_gpios;uart0_gpios定义GPIO2_C3功能复用为GPIO;
- BT,reset_gpio:配置蓝牙复位引脚BT_REG_ON为GPIO0_B1,高电平有效;
- BT,wake_gpio:配置主机唤醒蓝牙设备引脚BT_WAKE为GPIO2_D2,高电平有效;
- BT,wake_host_irq:配置蓝牙设备唤醒主机引脚BT_HOST_WAKE为GPIO0_A4,高电平有效;
- status:表示该设备状态为正常运行;
这里顺带看一下uart0_rts、uart0_gpios配置:
uart0_rts: uart0-rts { rockchip,pins = <2 RK_PC3 1 &pcfg_pull_none>; }; // 这个也是要新增的,默认没有这个配置 在&pinctrl下新增该子节点 wireless-bluetooth { uart0_gpios: uart0-gpios { rockchip,pins = <2 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>; }; };
4.3.4 配置内核
需要配置内核:
[*] Networking support ---> <*> RF switch subsystem support ---> <*> Rockchip RFKILL driver
4.4 rk808驱动配置
在arch/arm64/boot/dts/rockchip/rk3399-evb.dts中添加如下节点:
&i2c0 { clock-frequency = <400000>; i2c-scl-rising-time-ns = <160>; i2c-scl-falling-time-ns = <30>; status = "okay"; vdd_cpu_b: regulator@40 { compatible = "silergy,syr827"; reg = <0x40>; fcs,suspend-voltage-selector = <1>; pinctrl-names = "default"; pinctrl-0 = <&cpu_b_sleep>; regulator-always-on; regulator-boot-on; regulator-min-microvolt = <712500>; regulator-max-microvolt = <1500000>; regulator-name = "vdd_cpu_b"; regulator-ramp-delay = <1000>; vin-supply = <&vcc3v3_sys>; regulator-state-mem { regulator-off-in-suspend; }; }; vdd_gpu: regulator@41 { compatible = "silergy,syr828"; reg = <0x41>; fcs,suspend-voltage-selector = <1>; pinctrl-names = "default"; pinctrl-0 = <&gpu_sleep>; regulator-always-on; regulator-boot-on; regulator-min-microvolt = <712500>; regulator-max-microvolt = <1500000>; regulator-name = "vdd_gpu"; regulator-ramp-delay = <1000>; vin-supply = <&vcc3v3_sys>; regulator-state-mem { regulator-off-in-suspend; }; }; rk808: pmic@1b { compatible = "rockchip,rk808"; reg = <0x1b>; clock-output-names = "xin32k", "rtc_clko_wifi"; #clock-cells = <1>; interrupt-parent = <&gpio1>; interrupts = <21 IRQ_TYPE_LEVEL_LOW>; pinctrl-names = "default"; pinctrl-0 = <&pmic_int_l>; rockchip,system-power-controller; wakeup-source; vcc1-supply = <&vcc3v3_sys>; vcc2-supply = <&vcc3v3_sys>; vcc3-supply = <&vcc3v3_sys>; vcc4-supply = <&vcc3v3_sys>; vcc6-supply = <&vcc3v3_sys>; vcc7-supply = <&vcc3v3_sys>; vcc8-supply = <&vcc3v3_sys>; vcc9-supply = <&vcc3v3_sys>; vcc10-supply = <&vcc3v3_sys>; vcc11-supply = <&vcc3v3_sys>; vcc12-supply = <&vcc3v3_sys>; vddio-supply = <&vcc_3v0>; regulators { vdd_center: DCDC_REG1 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <750000>; regulator-max-microvolt = <1350000>; regulator-name = "vdd_center"; regulator-ramp-delay = <6001>; regulator-state-mem { regulator-off-in-suspend; }; }; vdd_cpu_l: DCDC_REG2 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <750000>; regulator-max-microvolt = <1350000>; regulator-name = "vdd_cpu_l"; regulator-ramp-delay = <6001>; regulator-state-mem { regulator-off-in-suspend; }; }; vcc_ddr: DCDC_REG3 { regulator-always-on; regulator-boot-on; regulator-name = "vcc_ddr"; regulator-state-mem { regulator-on-in-suspend; }; }; vcc_1v8: DCDC_REG4 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-name = "vcc_1v8"; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <1800000>; }; }; vcc1v8_cam: LDO_REG1 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-name = "vcc1v8_cam"; regulator-state-mem { regulator-off-in-suspend; }; }; vcc3v0_touch: LDO_REG2 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <3000000>; regulator-name = "vcc3v0_touch"; regulator-state-mem { regulator-off-in-suspend; }; }; vcc1v8_pmupll: LDO_REG3 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-name = "vcc1v8_pmupll"; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <1800000>; }; }; vcc_sdio: LDO_REG4 { regulator-always-on; regulator-boot-on; regulator-init-microvolt = <3000000>; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <3300000>; regulator-name = "vcc_sdio"; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <3000000>; }; }; vcca3v0_codec: LDO_REG5 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <3000000>; regulator-name = "vcca3v0_codec"; regulator-state-mem { regulator-off-in-suspend; }; }; vcc_1v5: LDO_REG6 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <1500000>; regulator-max-microvolt = <1500000>; regulator-name = "vcc_1v5"; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <1500000>; }; }; vcca1v8_codec: LDO_REG7 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <1800000>; regulator-max-microvolt = <1800000>; regulator-name = "vcca1v8_codec"; regulator-state-mem { regulator-off-in-suspend; }; }; vcc_3v0: LDO_REG8 { regulator-always-on; regulator-boot-on; regulator-min-microvolt = <3000000>; regulator-max-microvolt = <3000000>; regulator-name = "vcc_3v0"; regulator-state-mem { regulator-on-in-suspend; regulator-suspend-microvolt = <3000000>; }; }; vcc3v3_s3: SWITCH_REG1 { regulator-always-on; regulator-boot-on; regulator-name = "vcc3v3_s3"; regulator-state-mem { regulator-off-in-suspend; }; }; vcc3v3_s0: SWITCH_REG2 { regulator-always-on; regulator-boot-on; regulator-name = "vcc3v3_s0"; regulator-state-mem { regulator-off-in-suspend; }; }; }; }; };
同时移除该文件根节点下的vdd_center设备节点:
vdd_center: vdd-center { compatible = "pwm-regulator"; pwms = <&pwm3 0 25000 0>; regulator-name = "vdd_center"; regulator-min-microvolt = <800000>; regulator-max-microvolt = <1400000>; regulator-always-on; regulator-boot-on; status = "okay"; };
修改&pinctrl下pmic引脚配置节点:
pmic { cpu_b_sleep: cpu-b-sleep { rockchip,pins = <1 RK_PC1 RK_FUNC_GPIO &pcfg_pull_down>; }; gpu_sleep: gpu-sleep { rockchip,pins = <1 RK_PB6 RK_FUNC_GPIO &pcfg_pull_down>; }; pmic_int_l: pmic-int-l { rockchip,pins = <1 RK_PC5 RK_FUNC_GPIO &pcfg_pull_up>; }; };
4.5 配置内核
配置完内核之后记得保存配置:
存档:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# mv rk3399_defconfig ./arch/arm64/configs/
重新配置内核:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# make rk3399_defconfig
4.6 编译内核
在linux内核根目录下执行如下命令进行编译内核:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# make -j8
u-boot-2023.04路径下的mkimage工具拷贝过来,然后在命令行使用mkimage工具编译即可:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp ../u-boot-2023.04/tools/mkimage ./ root@zhengyang:/work/sambashare/rk3399/linux-6.3# ./mkimage -f kernel.its kernel.itb
需要注意的是这里一定不能指定-E参数,不然uboot在进行kernel镜像hash校验的时候就会失败。
五 烧录测试
给开发板上电,同时连接上网线,进入uboot命令行。
5.1 下载内核和WiFi固件
5.1.1 tftp下载内核
我们将内核拷贝到tftp文件目录:
root@zhengyang:/work/sambashare/rk3399/linux-6.3# cp kernel.itb /work/tftpboot/
接着通过uboot命令行将kernel.itb下到内存地址0x10000000处:
=> tftp 0x10000000 kernel.itb
通过mmc write命令将内核镜像烧录到eMMC第0x8000个扇区处:
=> mmc erase 0x8000 0xA000 => mmc write 0x10000000 0x8000 0xA000
5.1.2 下载WiFi固件
在uboot命令行运行boot命令启动内核:
=> boot
内核启动完成后,我们通过scp命令将宿主机/work/sambashare/rk3399/linux-6.3目录下的firmware文件夹拷贝到开发板/lib路径下;
root@rk3399:~# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/firmware /lib The authenticity of host '192.168.0.200 (192.168.0.200)' can't be established. ECDSA key fingerprint is SHA256:TKGBJPbpGmAJS0Ug3SjFGd7dfwtlUoYeyZi8p0JDBeE. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '192.168.0.200' (ECDSA) to the list of known hosts. root@192.168.0.200's password: rtl8852cu_config 100% 14 5.8KB/s 00:00 mt7662_rom_patch.bin 100% 26KB 5.5MB/s 00:00 rtl8852au_config 100% 14 5.7KB/s 00:00 fw_bcm4359c0_ag.bin 100% 563KB 10.6MB/s 00:00 fw_bcm43455c0_ag.bin 100% 487KB 10.5MB/s 00:00 README.md 100% 37 18.6KB/s 00:00 iwlwifi-3160-17.ucode 100% 897KB 10.8MB/s 00:00 vh264_mc 100% 36KB 6.7MB/s 00:00 vmpeg12_mc 100% 16KB 3.4MB/s 00:00 vmpeg4_mc_5 100% 16KB 3.8MB/s 00:00 vh265_mc 100% 16KB 4.1MB/s 00:00
.....
需要注意的是:此时使用的是有线网卡,必须连接网线,并且保证有线网卡能够工作,不然无法使用scp命令。
拷贝完成后,我们看一下/lib/firmware文件夹;
root@rk3399:~# ls /lib/firmware BCM4345C0.hcd meson BCM4345C5.hcd mt7601.bin README.md mt7601u.bin RTL8192SU mt7610u.bin ap6210 mt7662.bin ap6212 mt7662_rom_patch.bin ap6275p nvram_ap6255.txt brcm nvram_ap6256.txt bt_configure_pskey.ini nvram_ap6398s.txt bt_configure_rf.ini regulatory.db dvb-demod-mn88472-02.fw regulatory.db.p7s dvb-demod-mn88473-01.fw rkwifi dvb-demod-si2168-d60-01.fw rockchip dvb-fe-xc4000-1.4.1.fw rt2870.bin dvb-fe-xc4000-1.4.fw rtl8852au_config dvb-fe-xc5000-1.6.114.fw rtl8852au_fw dvb-fe-xc5000c-4.1.30.7.fw rtl8852bu_config dvb-tuner-si2141-a10-01.fw rtl8852bu_fw dvb-usb-tbs5520.fw rtl8852cu_config eagle_fw_ate_config_v19.bin rtl8852cu_fw eagle_fw_first_init_v19.bin rtl_bt eagle_fw_second_init_v19.bin rtl_nic edid rtlbt fw_bcm43455c0_ag.bin rtlwifi fw_bcm43455c0_ag_apsta.bin rtw88 fw_bcm43455c0_ag_p2p.bin s5p-mfc-v8.fw fw_bcm43456c5_ag.bin sdma fw_bcm43456c5_ag_apsta.bin ssv6051-sw.bin fw_bcm43456c5_ag_p2p.bin ssv6051-wifi.cfg fw_bcm4359c0_ag.bin ssv6x5x-sw.bin hp ssv6x5x-wifi.cfg imx ti-connectivity iwlwifi-3160-17.ucode uwe5621ds iwlwifi-3168-29.ucode uwe5622 iwlwifi-7260-17.ucode v4l-coda960-imx6dl.bin iwlwifi-7265-17.ucode v4l-coda960-imx6q.bin iwlwifi-7265D-29.ucode vpu iwlwifi-8000C-36.ucode wcnmodem.bin iwlwifi-8265-36.ucode wifi_2355b001_1ant.ini iwlwifi-9260-th-b0-jf-b0-46.ucode xc3028-v27.fw iwlwifi-cc-a0-59.ucode xc3028L-v36.fw iwlwifi-ty-a0-gf-a0-59.ucode xc4000-1.4.fw mediatek xr819 root@rk3399:~# ls /lib/firmware/brcm/ BCM4330B1.hcd BCM43342.hcd BCM4334B0.hcd BCM43430A1.hcd BCM4343A0.hcd BCM4345C0.radxa,zero2.hcd BCM4345C0_003.001.025.0162.0000_Generic_UART_37_4MHz_wlbga_ref_iLNA_iTR_eLG.hcd BCM4345C5.hcd BCM4356A2.hcd BCM4359C0.hcd BCM4362A2.hcd bcm4329.hcd bcm4330.hcd bcm43438-sdio.hcd brcmfmac-ap6330-sdio.bin brcmfmac-ap6330-sdio.txt brcmfmac4329-sdio.bin brcmfmac4329-sdio.txt brcmfmac4330-sdio.bin brcmfmac4330-sdio.rockchip,rk3318-box.txt brcmfmac4330-sdio.txt brcmfmac4334-sdio.bin brcmfmac4334-sdio.rockchip,rk3318-box.txt brcmfmac43342-sdio.bin brcmfmac43342-sdio.txt brcmfmac43362-sdio.bin brcmfmac43362-sdio.txt brcmfmac4339-sdio.1CK.txt brcmfmac4339-sdio.ZP.txt brcmfmac4339-sdio.bin brcmfmac4339-sdio.txt brcmfmac43430-sdio.bin brcmfmac43430-sdio.txt brcmfmac43430a0-sdio.bin brcmfmac43430a0-sdio.txt brcmfmac43455-sdio.bin brcmfmac43455-sdio.clm_blob brcmfmac43455-sdio.radxa,zero2.bin brcmfmac43455-sdio.radxa,zero2.txt brcmfmac43455-sdio.txt brcmfmac43456-sdio.bin brcmfmac43456-sdio.clm_blob brcmfmac43456-sdio.radxa,rockpi4b.bin brcmfmac43456-sdio.radxa,rockpi4b.txt brcmfmac43456-sdio.radxa,zero.bin brcmfmac43456-sdio.radxa,zero.txt brcmfmac43456-sdio.radxa,zero2.bin brcmfmac43456-sdio.radxa,zero2.txt brcmfmac43456-sdio.txt brcmfmac4356-sdio-nanopi-m4v2.bin brcmfmac4356-sdio-nanopi-m4v2.txt brcmfmac4356-sdio.bin brcmfmac4356-sdio.clm_blob brcmfmac4356-sdio.txt brcmfmac4359-sdio.bin brcmfmac4359-sdio.txt brcmfmac43752-sdio.bin brcmfmac43752-sdio.clm_blob brcmfmac43752-sdio.txt config.txt
接着,对于linux 6.3,我们需要提供固件brcmfmac4356-sdio.rockchip,rk3399-evb.bin,实际上就是对brcmfmac4356-sdio.bin进行重命名;
root@rk3399:~# cp /lib/firmware/brcm/brcmfmac4356-sdio-nanopi-m4v2.txt /lib/firmware/brcm/brcmfmac4356-sdio.rockchip,rk3399-evb.txt root@rk3399:~# cp /lib/firmware/brcm/brcmfmac4356-sdio.bin /lib/firmware/brcm/brcmfmac4356-sdio.rockchip,rk3399-evb.bin
接着我们将内核编译出的模块打包放到根文件系统/lib/modules/{uname -r命令查看}下面,内核启动时会自动加载,加载顺序如下;
- IEEE 802.11模块:net/wireless/cfg80211.ko;
- brcm驱动使用的工具:drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko;
- brcm驱动:drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko;
对于linux 6.3版本,还需要下面几个文件:
- drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/brcmfmac-wcc.ko;
- drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/brcmfmac-bca.ko;
- drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/brcmfmac-cyw.ko;
scp将驱动文件从宿主机拷贝到开发板:
root@rk3399:/lib# mkdir -p /lib/modules/6.3.0 root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/modules.builtin /lib/modules/6.3.0 root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/modules.order /lib/modules/6.3.0 root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/net/wireless/cfg80211.ko /lib/modules/6.3.0 root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko /lib/modules/6.3.0 root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko /lib/modules/6.3.0 root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/net/wireless/broadcom/brcm80211/brcmfmac/wcc/brcmfmac-wcc.ko /lib/modules/6.3.0 root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bca/brcmfmac-bca.ko /lib/modules/6.3.0 root@rk3399:/lib# scp -r root@192.168.0.200:/work/sambashare/rk3399/linux-6.3/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/brcmfmac-cyw.ko /lib/modules/6.3.0
内核启动的时候,使用/sbin/modprobe加载/lib/modules/{uname -r命令查看}下面的模块,modules.order文件指定了模块的加载顺序。此时开发板/lib/modules/6.3.0路径下:
root@rk3399:/lib/modules/6.3.0# ll total 4860 drwxr-xr-x 2 root root 4096 Jun 14 20:14 ./ drwxr-xr-x 3 root root 4096 Jun 14 20:12 ../ -rw-r--r-- 1 root root 60856 Jun 14 19:34 brcmfmac-bca.ko -rw-r--r-- 1 root root 60856 Jun 14 19:34 brcmfmac-cyw.ko -rw-r--r-- 1 root root 60856 Jun 14 19:34 brcmfmac-wcc.ko -rw-r--r-- 1 root root 1952848 Jun 14 01:01 brcmfmac.ko -rw-r--r-- 1 root root 64120 Jun 14 01:01 brcmutil.ko -rw-r--r-- 1 root root 2679360 Jun 13 22:32 cfg80211.ko -rw-r--r-- 1 root root 42147 Jun 14 19:43 modules.builtin -rw-r--r-- 1 root root 33053 Jun 14 19:44 modules.order
需要在加载模块之前建立该模块的依赖关系,也即必须用depmod来更新一下/lib/modules/$(uname -r)/modules.dep 文件;
root@rk3399:/lib/modules/6.3.0# depmod -a root@rk3399:/lib/modules/6.3.0# ll total 4948 drwxr-xr-x 2 root root 4096 Jun 14 20:16 ./ drwxr-xr-x 3 root root 4096 Jun 14 20:12 ../ -rw-r--r-- 1 root root 60856 Jun 14 19:34 brcmfmac-bca.ko -rw-r--r-- 1 root root 60856 Jun 14 19:34 brcmfmac-cyw.ko -rw-r--r-- 1 root root 60856 Jun 14 19:34 brcmfmac-wcc.ko -rw-r--r-- 1 root root 1952848 Jun 14 01:01 brcmfmac.ko -rw-r--r-- 1 root root 64120 Jun 14 01:01 brcmutil.ko -rw-r--r-- 1 root root 2679360 Jun 13 22:32 cfg80211.ko -rw-r--r-- 1 root root 842 Jun 14 20:16 modules.alias -rw-r--r-- 1 root root 937 Jun 14 20:16 modules.alias.bin -rw-r--r-- 1 root root 42147 Jun 14 19:43 modules.builtin -rw-r--r-- 1 root root 0 Jun 14 20:16 modules.builtin.alias.bin -rw-r--r-- 1 root root 46577 Jun 14 20:16 modules.builtin.bin -rw-r--r-- 1 root root 222 Jun 14 20:16 modules.dep -rw-r--r-- 1 root root 483 Jun 14 20:16 modules.dep.bin -rw-r--r-- 1 root root 0 Jun 14 20:16 modules.devname -rw-r--r-- 1 root root 33053 Jun 14 19:44 modules.order -rw-r--r-- 1 root root 55 Jun 14 20:16 modules.softdep -rw-r--r-- 1 root root 7567 Jun 14 20:16 modules.symbols -rw-r--r-- 1 root root 9252 Jun 14 20:16 modules.symbols.bin
5.2 启动内核
我们重新启动开发板,内核输出启动日志如下:
U-Boot TPL 2023.04 (Jun 12 2023 - 23:22:48) Channel 0: LPDDR3, 800MHz BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB Channel 1: LPDDR3, 800MHz BW=32 Col=10 Bk=8 CS0 Row=15 CS1 Row=15 CS=2 Die BW=16 Size=2048MB 256B stride Trying to boot from BOOTROM Returning to boot ROM... U-Boot SPL 2023.04 (Jun 12 2023 - 23:22:48 +0800) Trying to boot from MMC2 spl_load_fit_image: Skip load 'atf@5': image size is 0! cannot find image node 'atf@6': -1 NOTICE: BL31: v2.8(release):c194aa0 NOTICE: BL31: Built : 19:26:54, May 11 2023 U-Boot 2023.04 (Jun 12 2023 - 23:23:06 +0800) SoC: Rockchip rk3399 Reset cause: POR Model: Rockchip RK3399 Evaluation Board DRAM: 4 GiB (effective 3.9 GiB) Core: 254 devices, 27 uclasses, devicetree: separate MMC: mmc@fe320000: 1, mmc@fe330000: 0 Loading Environment from MMC... OK In: serial Out: serial Err: serial Model: Rockchip RK3399 Evaluation Board Net: eth0: ethernet@fe300000 Hit any key to stop autoboot: 0 => tftp 0x10000000 kernel.itb ethernet@fe300000 Waiting for PHY auto negotiation to complete.... done Speed: 100, full duplex Using ethernet@fe300000 device TFTP from server 192.168.0.200; our IP address is 192.168.0.105 Filename 'kernel.itb'. Load address: 0x10000000 Loading: ######T ##T ######################################################### ################################################################# ########################################################T ######### ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ################################################################# ########################T ######################################### ######################## 417 KiB/s done Bytes transferred = 12746728 (c27fe8 hex) => mmc erase 0x8000 0xA000 MMC erase: dev # 0, block # 32768, count 40960 ... 40960 blocks erased: OK => mmc write 0x10000000 0x8000 0xA000 MMC write: dev # 0, block # 32768, count 40960 ... 40960 blocks written: OK => bootm 0x10000000 ## Loading kernel from FIT Image at 10000000 ... Using 'conf-1' configuration Trying 'kernel' kernel subimage Description: Vanilla Linux kernel Type: Kernel Image Compression: gzip compressed Data Start: 0x100000e8 Data Size: 12687547 Bytes = 12.1 MiB Architecture: AArch64 OS: Linux Load Address: 0x00280000 Entry Point: 0x00280000 Hash algo: crc32 Hash value: 2456b90f Hash algo: sha1 Hash value: 9b8e3d90f8581ea780f23212a50a7ce5d8ebeea6 Verifying Hash Integrity ... crc32+ sha1+ OK bootm_find_os images.os.load=0x280000 bootm_find_os images.ep=0x280000 bootm_find_os images.os.image_start=0x100000e8 ## Loading fdt from FIT Image at 10000000 ... Using 'conf-1' configuration Trying 'fdt' fdt subimage Description: Flattened Device Tree blob Type: Flat Device Tree Compression: uncompressed Data Start: 0x10c19acc Data Size: 57279 Bytes = 55.9 KiB Architecture: AArch64 Load Address: 0x08300000 Hash algo: crc32 Hash value: 8e1f95ec Hash algo: sha1 Hash value: 3867f6d0aff0eb758f2b97d270b10e994b59e3fd Verifying Hash Integrity ... crc32+ sha1+ OK Loading fdt from 0x10c19acc to 0x08300000 Booting using the fdt blob at 0x8300000 Working FDT set to 8300000 Uncompressing Kernel Image kernel loaded at 0x00280000, end = 0x02725a00 bootm_load_os 0x20000000=0x5aa5f00f bootm_load_os 0x20000010=0x0 Loading Device Tree to 00000000f4601000, end 00000000f4611fbe ... OK Working FDT set to f4601000 ## Transferring control to Linux (at address 280000)... boot_jump_linux 0x280000=0xfa405a4d boot_jump_linux 0x280004=0x14627c27 boot_jump_linux 0x280014=0x0 boot_jump_linux 0xf4601000=0xedfe0dd0 boot_jump_linux 0xf4601004=0xf00000 boot_jump_linux 0xf4601014=0x11000000 Starting kernel ... [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034] [ 0.000000] Linux version 6.3.0 (root@zhengyang) (arm-linux-gcc (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 12.2.1 20221205, GNU ld (Arm GNU Toolchain 12.2.Rel1 (Build arm-12.24)) 2.39.0.20221210) #14 SMP PREEMPT Wed Jun 14 20:34:20 CST 2023 [ 0.000000] Machine model: Rockchip RK3399 Evaluation Board [ 0.000000] earlycon: uart8250 at MMIO32 0x00000000ff1a0000 (options '') [ 0.000000] printk: bootconsole [uart8250] enabled [ 0.000000] efi: UEFI not found. [ 0.000000] [Firmware Bug]: Kernel image misaligned at boot, please fix your bootloader! [ 0.000000] NUMA: No NUMA configuration found [ 0.000000] NUMA: Faking a node at [mem 0x0000000000200000-0x00000000f7ffffff] [ 0.000000] NUMA: NODE_DATA [mem 0xf77e69c0-0xf77e8fff] [ 0.000000] Zone ranges: [ 0.000000] DMA [mem 0x0000000000200000-0x00000000f7ffffff] [ 0.000000] DMA32 empty [ 0.000000] Normal empty [ 0.000000] Movable zone start for each node [ 0.000000] Early memory node ranges [ 0.000000] node 0: [mem 0x0000000000200000-0x00000000f7ffffff] [ 0.000000] Initmem setup node 0 [mem 0x0000000000200000-0x00000000f7ffffff] [ 0.000000] On node 0, zone DMA: 512 pages in unavailable ranges [ 0.000000] cma: Reserved 32 MiB at 0x00000000f5600000 [ 0.000000] psci: probing for conduit method from DT. [ 0.000000] psci: PSCIv1.1 detected in firmware. [ 0.000000] psci: Using standard PSCI v0.2 function IDs [ 0.000000] psci: MIGRATE_INFO_TYPE not supported. [ 0.000000] psci: SMC Calling Convention v1.2 [ 0.000000] percpu: Embedded 22 pages/cpu s50472 r8192 d31448 u90112 [ 0.000000] Detected VIPT I-cache on CPU0 [ 0.000000] CPU features: detected: GIC system register CPU interface [ 0.000000] CPU features: detected: ARM erratum 845719 [ 0.000000] alternatives: applying boot alternatives [ 0.000000] Fallback order for Node 0: 0 [ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 999432 [ 0.000000] Policy zone: DMA [ 0.000000] Kernel command line: earlycon=uart8250,mmio32,0xff1a0000 console=tty0 console=ttyS2,115200n8 root=PARTUUID=B921B045-1D rw rootwait rootfstype=ext4 init=/sbin/init [ 0.000000] Dentry cache hash table entries: 524288 (order: 10, 4194304 bytes, linear) [ 0.000000] Inode-cache hash table entries: 262144 (order: 9, 2097152 bytes, linear) [ 0.000000] mem auto-init: stack:all(zero), heap alloc:off, heap free:off [ 0.000000] Memory: 3911696K/4061184K available (15488K kernel code, 4118K rwdata, 9624K rodata, 8128K init, 598K bss, 116720K reserved, 32768K cma-reserved) [ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=6, Nodes=1 [ 0.000000] rcu: Preemptible hierarchical RCU implementation. [ 0.000000] rcu: RCU event tracing is enabled. [ 0.000000] rcu: RCU restricting CPUs from NR_CPUS=256 to nr_cpu_ids=6. [ 0.000000] Trampoline variant of Tasks RCU enabled. [ 0.000000] Tracing variant of Tasks RCU enabled. [ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies. [ 0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=6 [ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0 [ 0.000000] GICv3: GIC: Using split EOI/Deactivate mode [ 0.000000] GICv3: 256 SPIs implemented [ 0.000000] GICv3: 0 Extended SPIs implemented [ 0.000000] Root IRQ handler: gic_handle_irq [ 0.000000] GICv3: GICv3 features: 16 PPIs [ 0.000000] GICv3: CPU0: found redistributor 0 region 0:0x00000000fef00000 [ 0.000000] ITS [mem 0xfee20000-0xfee3ffff] [ 0.000000] ITS@0x00000000fee20000: allocated 65536 Devices @2880000 (flat, esz 8, psz 64K, shr 0) [ 0.000000] ITS: using cache flushing for cmd queue [ 0.000000] GICv3: using LPI property table @0x0000000002840000 [ 0.000000] GIC: using cache flushing for LPI property table [ 0.000000] GICv3: CPU0: using allocated LPI pending table @0x0000000002850000 [ 0.000000] GICv3: GIC: PPI partition interrupt-partition-0[0] { /cpus/cpu@0[0] /cpus/cpu@1[1] /cpus/cpu@2[2] /cpus/cpu@3[3] } [ 0.000000] GICv3: GIC: PPI partition interrupt-partition-1[1] { /cpus/cpu@100[4] /cpus/cpu@101[5] } [ 0.000000] rcu: srcu_init: Setting srcu_struct sizes based on contention. [ 0.000000] arch_timer: cp15 timer(s) running at 24.00MHz (phys). [ 0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns [ 0.000001] sched_clock: 56 bits at 24MHz, resolution 41ns, wraps every 4398046511097ns [ 0.012892] Console: colour dummy device 80x25 [ 0.017797] printk: console [tty0] enabled [ 0.023353] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=96000) [ 0.034681] pid_max: default: 32768 minimum: 301 [ 0.039876] LSM: initializing lsm=capability,integrity [ 0.045709] Mount-cache hash table entries: 8192 (order: 4, 65536 bytes, linear) [ 0.053898] Mountpoint-cache hash table entries: 8192 (order: 4, 65536 bytes, linear) [ 0.065235] cblist_init_generic: Setting adjustable number of callback queues. [ 0.073260] cblist_init_generic: Setting shift to 3 and lim to 1. [ 0.080101] cblist_init_generic: Setting shift to 3 and lim to 1. [ 0.087152] rcu: Hierarchical SRCU implementation. [ 0.092458] rcu: Max phase no-delay instances is 1000. [ 0.098612] Platform MSI: msi-controller@fee20000 domain created [ 0.105656] fsl-mc MSI: msi-controller@fee20000 domain created [ 0.118203] EFI services will not be available. [ 0.123736] smp: Bringing up secondary CPUs ... [ 0.129551] Detected VIPT I-cache on CPU1 [ 0.129635] GICv3: CPU1: found redistributor 1 region 0:0x00000000fef20000 [ 0.129658] GICv3: CPU1: using allocated LPI pending table @0x0000000002860000 [ 0.129717] CPU1: Booted secondary processor 0x0000000001 [0x410fd034] [ 0.130598] Detected VIPT I-cache on CPU2 [ 0.130661] GICv3: CPU2: found redistributor 2 region 0:0x00000000fef40000 [ 0.130679] GICv3: CPU2: using allocated LPI pending table @0x0000000002870000 [ 0.130719] CPU2: Booted secondary processor 0x0000000002 [0x410fd034] [ 0.131500] Detected VIPT I-cache on CPU3 [ 0.131562] GICv3: CPU3: found redistributor 3 region 0:0x00000000fef60000 [ 0.131581] GICv3: CPU3: using allocated LPI pending table @0x0000000002900000 [ 0.131618] CPU3: Booted secondary processor 0x0000000003 [0x410fd034] [ 0.132431] CPU features: detected: Spectre-v2 [ 0.132444] CPU features: detected: Spectre-v3a [ 0.132454] CPU features: detected: Spectre-BHB [ 0.132465] CPU features: detected: ARM erratum 1742098 [ 0.132473] CPU features: detected: ARM errata 1165522, 1319367, or 1530923 [ 0.132480] Detected PIPT I-cache on CPU4 [ 0.132551] GICv3: CPU4: found redistributor 100 region 0:0x00000000fef80000 [ 0.132570] GICv3: CPU4: using allocated LPI pending table @0x0000000002910000 [ 0.132612] CPU4: Booted secondary processor 0x0000000100 [0x410fd082] [ 0.133503] Detected PIPT I-cache on CPU5 [ 0.133565] GICv3: CPU5: found redistributor 101 region 0:0x00000000fefa0000 [ 0.133583] GICv3: CPU5: using allocated LPI pending table @0x0000000002920000 [ 0.133617] CPU5: Booted secondary processor 0x0000000101 [0x410fd082] [ 0.133732] smp: Brought up 1 node, 6 CPUs [ 0.302581] SMP: Total of 6 processors activated. [ 0.307781] CPU features: detected: 32-bit EL0 Support [ 0.313470] CPU features: detected: 32-bit EL1 Support [ 0.319146] CPU features: detected: CRC32 instructions [ 0.324949] CPU: All CPU(s) started at EL2 [ 0.329508] alternatives: applying system-wide alternatives [ 0.339503] devtmpfs: initialized [ 0.355154] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns [ 0.365917] futex hash table entries: 2048 (order: 5, 131072 bytes, linear) [ 0.374383] pinctrl core: initialized pinctrl subsystem [ 0.383025] DMI not present or invalid. [ 0.388137] NET: Registered PF_NETLINK/PF_ROUTE protocol family [ 0.396215] DMA: preallocated 512 KiB GFP_KERNEL pool for atomic allocations [ 0.404445] DMA: preallocated 512 KiB GFP_KERNEL|GFP_DMA pool for atomic allocations [ 0.413369] DMA: preallocated 512 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations [ 0.422139] audit: initializing netlink subsys (disabled) [ 0.428309] audit: type=2000 audit(0.296:1): state=initialized audit_enabled=0 res=1 [ 0.429906] thermal_sys: Registered thermal governor 'step_wise' [ 0.436870] thermal_sys: Registered thermal governor 'power_allocator' [ 0.443558] cpuidle: using governor menu [ 0.455310] hw-breakpoint: found 6 breakpoint and 4 watchpoint registers. [ 0.462995] ASID allocator initialised with 65536 entries [ 0.471673] Serial: AMBA PL011 UART driver [ 0.503616] platform fe330000.mmc: Fixed dependency cycle(s) with /syscon@ff770000/phy@f780 [ 0.517192] platform ff940000.hdmi: Fixed dependency cycle(s) with /vop@ff8f0000/port/endpoint@2 [ 0.526883] platform ff940000.hdmi: Fixed dependency cycle(s) with /vop@ff900000/port/endpoint@2 [ 0.542451] gpio gpiochip0: Static allocation of GPIO base is deprecated, use dynamic allocation. [ 0.552542] rockchip-gpio ff720000.gpio: probed /pinctrl/gpio@ff720000 [ 0.560195] gpio gpiochip1: Static allocation of GPIO base is deprecated, use dynamic allocation. [ 0.570172] rockchip-gpio ff730000.gpio: probed /pinctrl/gpio@ff730000 [ 0.577739] gpio gpiochip2: Static allocation of GPIO base is deprecated, use dynamic allocation. [ 0.587696] rockchip-gpio ff780000.gpio: probed /pinctrl/gpio@ff780000 [ 0.595332] gpio gpiochip3: Static allocation of GPIO base is deprecated, use dynamic allocation. [ 0.605290] rockchip-gpio ff788000.gpio: probed /pinctrl/gpio@ff788000 [ 0.612844] gpio gpiochip4: Static allocation of GPIO base is deprecated, use dynamic allocation. [ 0.622806] rockchip-gpio ff790000.gpio: probed /pinctrl/gpio@ff790000 [ 0.635423] KASLR disabled due to lack of seed [ 0.641158] HugeTLB: registered 1.00 GiB page size, pre-allocated 0 pages [ 0.648634] HugeTLB: 0 KiB vmemmap can be freed for a 1.00 GiB page [ 0.655544] HugeTLB: registered 32.0 MiB page size, pre-allocated 0 pages [ 0.663010] HugeTLB: 0 KiB vmemmap can be freed for a 32.0 MiB page [ 0.669915] HugeTLB: registered 2.00 MiB page size, pre-allocated 0 pages [ 0.677381] HugeTLB: 0 KiB vmemmap can be freed for a 2.00 MiB page [ 0.684284] HugeTLB: registered 64.0 KiB page size, pre-allocated 0 pages [ 0.691749] HugeTLB: 0 KiB vmemmap can be freed for a 64.0 KiB page [ 0.700530] ACPI: Interpreter disabled. [ 0.710333] iommu: Default domain type: Translated [ 0.715715] iommu: DMA domain TLB invalidation policy: strict mode [ 0.722914] SCSI subsystem initialized [ 0.727487] usbcore: registered new interface driver usbfs [ 0.733564] usbcore: registered new interface driver hub [ 0.739447] usbcore: registered new device driver usb [ 0.746634] pps_core: LinuxPPS API ver. 1 registered [ 0.752101] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it> [ 0.762175] PTP clock support registered [ 0.766732] EDAC MC: Ver: 3.0.0 [ 0.771240] scmi_core: SCMI protocol bus registered [ 0.777852] FPGA manager framework [ 0.781717] Advanced Linux Sound Architecture Driver Initialized. [ 0.789844] clocksource: Switched to clocksource arch_sys_counter [ 0.796788] VFS: Disk quotas dquot_6.6.0 [ 0.801158] VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes) [ 0.808940] pnp: PnP ACPI: disabled [ 0.822713] NET: Registered PF_INET protocol family [ 0.828370] IP idents hash table entries: 65536 (order: 7, 524288 bytes, linear) [ 0.840820] tcp_listen_portaddr_hash hash table entries: 2048 (order: 3, 32768 bytes, linear) [ 0.850337] Table-perturb hash table entries: 65536 (order: 6, 262144 bytes, linear) [ 0.858879] TCP established hash table entries: 32768 (order: 6, 262144 bytes, linear) [ 0.867935] TCP bind hash table entries: 32768 (order: 8, 1048576 bytes, linear) [ 0.877345] TCP: Hash tables configured (established 32768 bind 32768) [ 0.884659] UDP hash table entries: 2048 (order: 4, 65536 bytes, linear) [ 0.892186] UDP-Lite hash table entries: 2048 (order: 4, 65536 bytes, linear) [ 0.900321] NET: Registered PF_UNIX/PF_LOCAL protocol family [ 0.907007] RPC: Registered named UNIX socket transport module. [ 0.913542] RPC: Registered udp transport module. [ 0.918727] RPC: Registered tcp transport module. [ 0.923904] RPC: Registered tcp NFSv4.1 backchannel transport module. [ 0.932158] hw perfevents: enabled with armv8_cortex_a53 PMU driver, 7 counters available [ 0.941644] hw perfevents: enabled with armv8_cortex_a72 PMU driver, 7 counters available [ 0.951273] kvm [1]: IPA Size Limit: 40 bits [ 0.958189] kvm [1]: vgic-v2@fff20000 [ 0.962261] kvm [1]: GIC system register CPU interface enabled [ 0.968710] kvm [1]: vgic interrupt IRQ18 [ 0.973184] kvm [1]: Hyp mode initialized successfully [ 0.980794] Initialise system trusted keyrings [ 0.985974] workingset: timestamp_bits=42 max_order=20 bucket_order=0 [ 0.993485] squashfs: version 4.0 (2009/01/31) Phillip Lougher [ 1.000258] NFS: Registering the id_resolver key type [ 1.005948] Key type id_resolver registered [ 1.010561] Key type id_legacy registered [ 1.014998] nfs4filelayout_init: NFSv4 File Layout Driver Registering... [ 1.022375] nfs4flexfilelayout_init: NFSv4 Flexfile Layout Driver Registering... [ 1.030714] 9p: Installing v9fs 9p2000 file system support [ 1.100797] Key type asymmetric registered [ 1.105313] Asymmetric key parser 'x509' registered [ 1.110738] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 244) [ 1.118890] io scheduler mq-deadline registered [ 1.123877] io scheduler kyber registered [ 1.128337] io scheduler bfq registered [ 1.156432] EINJ: ACPI disabled. [ 1.185972] dma-pl330 ff6d0000.dma-controller: Loaded driver for PL330 DMAC-241330 [ 1.194340] dma-pl330 ff6d0000.dma-controller: DBUFF-32x8bytes Num_Chans-6 Num_Peri-12 Num_Events-12 [ 1.206231] dma-pl330 ff6e0000.dma-controller: Loaded driver for PL330 DMAC-241330 [ 1.214587] dma-pl330 ff6e0000.dma-controller: DBUFF-128x8bytes Num_Chans-8 Num_Peri-20 Num_Events-16 [ 1.246170] Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled [ 1.256383] printk: console [ttyS2] disabled [ 1.261183] ff1a0000.serial: ttyS2 at MMIO 0xff1a0000 (irq = 39, base_baud = 1500000) is a 16550A [ 1.271170] printk: console [ttyS2] enabled [ 1.271170] printk: console [ttyS2] enabled [ 1.280586] printk: bootconsole [uart8250] disabled [ 1.280586] printk: bootconsole [uart8250] disabled [ 1.312410] SuperH (H)SCI(F) driver initialized [ 1.318507] msm_serial: driver initialized [ 1.329942] rockchip-vop ff8f0000.vop: Adding to iommu group 2 [ 1.337123] rockchip-vop ff900000.vop: Adding to iommu group 3 [ 1.348969] rockchip-drm display-subsystem: bound ff8f0000.vop (ops vop_component_ops) [ 1.357967] [drm] unsupported AFBC format[3231564e] [ 1.364518] rockchip-drm display-subsystem: bound ff900000.vop (ops vop_component_ops) [ 1.373531] dwhdmi-rockchip ff940000.hdmi: supply avdd-0v9 not found, using dummy regulator [ 1.383031] dwhdmi-rockchip ff940000.hdmi: supply avdd-1v8 not found, using dummy regulator [ 1.400614] loop: module loaded [ 1.417506] tun: Universal TUN/TAP device driver, 1.6 [ 1.429728] rk_gmac-dwmac fe300000.ethernet: IRQ eth_wake_irq not found [ 1.437173] rk_gmac-dwmac fe300000.ethernet: IRQ eth_lpi not found [ 1.444196] rk_gmac-dwmac fe300000.ethernet: PTP uses main clock [ 1.451119] rk_gmac-dwmac fe300000.ethernet: clock input or output? (input). [ 1.459029] rk_gmac-dwmac fe300000.ethernet: TX delay(0x28). [ 1.465375] rk_gmac-dwmac fe300000.ethernet: RX delay(0x11). [ 1.471725] rk_gmac-dwmac fe300000.ethernet: integrated PHY? (no). [ 1.478692] rk_gmac-dwmac fe300000.ethernet: cannot get clock clk_mac_speed [ 1.486493] rk_gmac-dwmac fe300000.ethernet: clock input from PHY [ 1.498329] rk_gmac-dwmac fe300000.ethernet: init for RGMII [ 1.504808] rk_gmac-dwmac fe300000.ethernet: User ID: 0x10, Synopsys ID: 0x35 [ 1.512829] rk_gmac-dwmac fe300000.ethernet: DWMAC1000 [ 1.518688] rk_gmac-dwmac fe300000.ethernet: DMA HW capability register supported [ 1.527072] rk_gmac-dwmac fe300000.ethernet: RX Checksum Offload Engine supported [ 1.535454] rk_gmac-dwmac fe300000.ethernet: COE Type 2 [ 1.541311] rk_gmac-dwmac fe300000.ethernet: TX Checksum insertion supported [ 1.549205] rk_gmac-dwmac fe300000.ethernet: Wake-Up On Lan supported [ 1.556493] rk_gmac-dwmac fe300000.ethernet: Normal descriptors [ 1.563134] rk_gmac-dwmac fe300000.ethernet: Ring mode enabled [ 1.569670] rk_gmac-dwmac fe300000.ethernet: Enable RX Mitigation via HW Watchdog Timer [ 1.668685] RTL8211E Gigabit Ethernet stmmac-0:00: attached PHY driver (mii_bus:phy_addr=stmmac-0:00, irq=POLL) [ 1.680056] RTL8211E Gigabit Ethernet stmmac-0:01: attached PHY driver (mii_bus:phy_addr=stmmac-0:01, irq=POLL) [ 1.695003] VFIO - User Level meta-driver version: 0.3 [ 1.708208] ehci-platform fe3c0000.usb: EHCI Host Controller [ 1.708240] ohci-platform fe3e0000.usb: Generic Platform OHCI controller [ 1.708276] ehci-platform fe380000.usb: EHCI Host Controller [ 1.708296] ohci-platform fe3a0000.usb: Generic Platform OHCI controller [ 1.708303] ehci-platform fe380000.usb: new USB bus registered, assigned bus number 1 [ 1.708337] ohci-platform fe3a0000.usb: new USB bus registered, assigned bus number 2 [ 1.708422] ehci-platform fe380000.usb: irq 48, io mem 0xfe380000 [ 1.708525] ohci-platform fe3a0000.usb: irq 50, io mem 0xfe3a0000 [ 1.709336] usbcore: registered new interface driver usb-storage [ 1.714592] ehci-platform fe3c0000.usb: new USB bus registered, assigned bus number 3 [ 1.714719] ehci-platform fe3c0000.usb: irq 47, io mem 0xfe3c0000 [ 1.716436] i2c_dev: i2c /dev entries driver [ 1.722424] ohci-platform fe3e0000.usb: new USB bus registered, assigned bus number 4 [ 1.725854] ehci-platform fe380000.usb: USB 2.0 started, EHCI 1.00 [ 1.726970] hub 1-0:1.0: USB hub found [ 1.727030] hub 1-0:1.0: 1 port detected [ 1.741863] ehci-platform fe3c0000.usb: USB 2.0 started, EHCI 1.00 [ 1.745412] ohci-platform fe3e0000.usb: irq 49, io mem 0xfe3e0000 [ 1.754387] hub 3-0:1.0: USB hub found [ 1.836757] hub 3-0:1.0: 1 port detected [ 1.842170] hub 2-0:1.0: USB hub found [ 1.846432] hub 2-0:1.0: 1 port detected [ 1.852256] hub 4-0:1.0: USB hub found [ 1.856600] hub 4-0:1.0: 1 port detected [ 2.149872] usb 2-1: new low-speed USB device number 2 using ohci-platform [ 2.421863] usb 4-1: new full-speed USB device number 2 using ohci-platform [ 2.725866] rk3x-i2c ff3c0000.i2c: timeout, ipd: 0x00, state: 1 [ 2.732541] fan53555-regulator 0-0040: Failed to get chip ID! [ 2.739085] fan53555-regulator: probe of 0-0040 failed with error -110 [ 2.747324] fan53555-regulator 0-0041: FAN53555 Option[8] Rev[1] Detected! [ 2.756588] i2c 0-001b: Fixed dependency cycle(s) with /i2c@ff3c0000/pmic@1b/regulators/LDO_REG8 [ 2.767288] rk808 0-001b: chip id: 0x0 [ 2.776096] rk808-regulator rk808-regulator.1.auto: there is no dvs0 gpio [ 2.783784] rk808-regulator rk808-regulator.1.auto: there is no dvs1 gpio [ 2.795284] vcc3v0_touch: Bringing 1800000uV into 3000000-3000000uV [ 2.805915] vcca3v0_codec: Bringing 1800000uV into 3000000-3000000uV [ 2.815379] vcca1v8_codec: Bringing 800000uV into 1800000-1800000uV [ 2.833234] dw_wdt ff848000.watchdog: No valid TOPs array specified [ 2.842715] ghes_edac: GHES probing device list is empty [ 2.843072] cpu cpu0: OPP table can't be empty [ 2.857950] sdhci: Secure Digital Host Controller Interface driver [ 2.864903] sdhci: Copyright(c) Pierre Ossman [ 2.871274] Synopsys Designware Multimedia Card Interface Driver [ 2.880114] sdhci-pltfm: SDHCI platform and OF driver helper [ 2.880293] dwmmc_rockchip fe310000.mmc: IDMAC supports 32-bit address mode. [ 2.889407] mmc0: CQHCI version 5.10 [ 2.894846] dwmmc_rockchip fe310000.mmc: Using internal DMA controller. [ 2.899050] ledtrig-cpu: registered to indicate activity on CPUs [ 2.905915] dwmmc_rockchip fe310000.mmc: Version ID is 270a [ 2.905997] dwmmc_rockchip fe310000.mmc: DW MMC controller at irq 64,32 bit host data width,256 deep fifo [ 2.914768] SMCCC: SOC_ID: ARCH_SOC_ID not implemented, skipping .... [ 2.919381] dwmmc_rockchip fe310000.mmc: allocated mmc-pwrseq [ 2.925371] mmc0: SDHCI controller on fe330000.mmc [fe330000.mmc] using ADMA [ 2.938152] input: SEMICO USB Keyboard as /devices/platform/fe3a0000.usb/usb2/2-1/2-1:1.0/0003:1A2C:4D7E.0001/input/input0 [ 2.943519] mmc_host mmc1: card is non-removable. [ 2.982147] mmc_host mmc1: Bus speed (slot 0) = 400000Hz (slot req 400000Hz, actual 400000HZ div = 0) [ 2.992770] mmc0: Command Queue Engine enabled [ 2.997828] mmc0: new HS400 Enhanced strobe MMC card at address 0001 [ 3.005983] mmcblk0: mmc0:0001 AJNB4R 14.6 GiB [ 3.014899] hid-generic 0003:1A2C:4D7E.0001: input: USB HID v1.10 Keyboard [SEMICO USB Keyboard] on usb-fe3a0000.usb-1/input0 [ 3.015750] mmcblk0: p1 p2 p3 p4 p5 [ 3.033483] mmcblk0boot0: mmc0:0001 AJNB4R 4.00 MiB [ 3.038172] input: SEMICO USB Keyboard Consumer Control as /devices/platform/fe3a0000.usb/usb2/2-1/2-1:1.1/0003:1A2C:4D7E.0002/input/input1 [ 3.040799] mmcblk0boot1: mmc0:0001 AJNB4R 4.00 MiB [ 3.060450] mmcblk0rpmb: mmc0:0001 AJNB4R 4.00 MiB, chardev (234:0) [ 3.106521] mmc_host mmc1: Bus speed (slot 0) = 148500000Hz (slot req 150000000Hz, actual 148500000HZ div = 0) [ 3.118765] input: SEMICO USB Keyboard System Control as /devices/platform/fe3a0000.usb/usb2/2-1/2-1:1.1/0003:1A2C:4D7E.0002/input/input2 [ 3.133117] input: SEMICO USB Keyboard as /devices/platform/fe3a0000.usb/usb2/2-1/2-1:1.1/0003:1A2C:4D7E.0002/input/input4 [ 3.145893] hid-generic 0003:1A2C:4D7E.0002: input: USB HID v1.10 Keyboard [SEMICO USB Keyboard] on usb-fe3a0000.usb-1/input1 [ 3.164336] input: Logitech USB Receiver as /devices/platform/fe3e0000.usb/usb4/4-1/4-1:1.0/0003:046D:C52B.0003/input/input5 [ 3.234891] hid-generic 0003:046D:C52B.0003: input: USB HID v1.11 Keyboard [Logitech USB Receiver] on usb-fe3e0000.usb-1/input0 [ 3.259392] input: Logitech USB Receiver Mouse as /devices/platform/fe3e0000.usb/usb4/4-1/4-1:1.1/0003:046D:C52B.0004/input/input6 [ 3.273016] input: Logitech USB Receiver Consumer Control as /devices/platform/fe3e0000.usb/usb4/4-1/4-1:1.1/0003:046D:C52B.0004/input/input7 [ 3.346966] input: Logitech USB Receiver System Control as /devices/platform/fe3e0000.usb/usb4/4-1/4-1:1.1/0003:046D:C52B.0004/input/input8 [ 3.361634] hid-generic 0003:046D:C52B.0004: input: USB HID v1.11 Mouse [Logitech USB Receiver] on usb-fe3e0000.usb-1/input1 [ 3.379293] hid-generic 0003:046D:C52B.0005: device has no listeners, quitting [ 3.387559] usbcore: registered new interface driver usbhid [ 3.393812] usbhid: USB HID core driver [ 3.403185] dwmmc_rockchip fe310000.mmc: Successfully tuned phase to 208 [ 3.416285] mmc1: new ultra high speed SDR104 SDIO card at address 0001 [ 3.419820] NET: Registered PF_PACKET protocol family [ 3.429513] [BT_RFKILL]: Enter rfkill_rk_init [ 3.434407] [WLAN_RFKILL]: Enter rfkill_wlan_init [ 3.439993] [WLAN_RFKILL]: Enter rfkill_wlan_probe [ 3.445414] [WLAN_RFKILL]: wlan_platdata_parse_dt: wifi_chip_type = ap6356 [ 3.453121] [WLAN_RFKILL]: wlan_platdata_parse_dt: enable wifi power control. [ 3.461114] [WLAN_RFKILL]: wlan_platdata_parse_dt: wifi power controled by gpio. [ 3.469450] [WLAN_RFKILL]: wlan_platdata_parse_dt: WIFI,host_wake_irq = 3, flags = 0. [ 3.478232] [WLAN_RFKILL]: wlan_platdata_parse_dt: The ref_wifi_clk not found ! [ 3.486421] [WLAN_RFKILL]: rfkill_wlan_probe: init gpio [ 3.492276] [WLAN_RFKILL]: rfkill_set_wifi_bt_power: 1 [ 3.498036] [WLAN_RFKILL]: Exit rfkill_wlan_probe [ 3.503936] [BT_RFKILL]: bluetooth_platdata_parse_dt: get property: uart_rts_gpios = 83. [ 3.513055] [BT_RFKILL]: bluetooth_platdata_parse_dt: get property: BT,reset_gpio = 9. [ 3.521946] [BT_RFKILL]: bluetooth_platdata_parse_dt: get property: BT,wake_gpio = 90. [ 3.530847] [BT_RFKILL]: bluetooth_platdata_parse_dt: get property: BT,wake_host_irq = 4. [ 3.540083] [BT_RFKILL]: Request irq for bt wakeup host [ 3.546113] [BT_RFKILL]: ** disable irq [ 3.546158] [BT_RFKILL]: BT_WAKE_HOST IRQ fired [ 3.555645] [BT_RFKILL]: bt shut off power [ 3.586087] [BT_RFKILL]: bt_default device registered. [ 3.592074] 9pnet: Installing 9P2000 support [ 3.596949] Key type dns_resolver registered [ 3.623490] registered taskstats version 1 [ 3.628478] Loading compiled-in X.509 certificates [ 3.679746] rockchip-drm display-subsystem: bound ff8f0000.vop (ops vop_component_ops) [ 3.689939] rockchip-drm display-subsystem: bound ff900000.vop (ops vop_component_ops) [ 3.699038] dwhdmi-rockchip ff940000.hdmi: supply avdd-0v9 not found, using dummy regulator [ 3.708657] dwhdmi-rockchip ff940000.hdmi: supply avdd-1v8 not found, using dummy regulator [ 3.718293] dwhdmi-rockchip ff940000.hdmi: Detected HDMI TX controller v2.11a with HDCP (DWC HDMI 2.0 TX PHY) [ 3.732472] rockchip-drm display-subsystem: bound ff940000.hdmi (ops dw_hdmi_rockchip_ops) [ 3.744296] [drm] Initialized rockchip 1.0.0 20140818 for display-subsystem on minor 0 [ 3.879654] Console: switching to colour frame buffer device 240x67 [ 3.924980] rockchip-drm display-subsystem: [drm] fb0: rockchipdrmfb frame buffer device [ 3.937003] ALSA device list: [ 3.940413] No soundcards found. [ 3.945544] dw-apb-uart ff1a0000.serial: forbid DMA for kernel console [ 4.194810] EXT4-fs (mmcblk0p5): 2 orphan inodes deleted [ 4.201578] EXT4-fs (mmcblk0p5): recovery complete [ 4.215167] EXT4-fs (mmcblk0p5): mounted filesystem 816ef390-7441-461e-b5df-547b6e69bd80 with ordered data mode. Quota mode: none. [ 4.229338] VFS: Mounted root (ext4 filesystem) on device 179:5. [ 4.239519] devtmpfs: mounted [ 4.247610] Freeing unused kernel memory: 8128K [ 4.254152] Run /sbin/init as init process [ 4.401298] systemd[1]: System time before build time, advancing clock. [ 4.455421] systemd[1]: systemd 245.4-4ubuntu3.21 running in system mode. (+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid) [ 4.482560] systemd[1]: Detected architecture arm64. Welcome to Ubuntu 20.04.4 LTS! [ 4.578861] systemd[1]: Set hostname to <rk3399>. [ 4.620040] memfd_create() without MFD_EXEC nor MFD_NOEXEC_SEAL, pid=1 'systemd' [ 4.859185] systemd[1]: Binding to IPv6 address not available since kernel does not support IPv6. [ 4.870018] systemd[1]: Binding to IPv6 address not available since kernel does not support IPv6. [ 7.250085] random: crng init done [ 7.257172] systemd[1]: Created slice system-getty.slice. [ OK ] Created slice system-getty.slice. [ 7.283606] systemd[1]: Created slice system-modprobe.slice. [ OK ] Created slice system-modprobe.slice. [ 7.311602] systemd[1]: Created slice system-serial\x2dgetty.slice. [ OK ] Created slice system-serial\x2dgetty.slice. [ 7.335453] systemd[1]: Created slice User and Session Slice. [ OK ] Created slice User and Session Slice. [ 7.358591] systemd[1]: Started Forward Password Requests to Wall Directory Watch. [ OK ] Started Forward Password R…uests to Wall Directory Watch. [ 7.386407] systemd[1]: Condition check resulted in Arbitrary Executable File Formats File System Automount Point being skipped. [ 7.400495] systemd[1]: Reached target User and Group Name Lookups. [ OK ] Reached target User and Group Name Lookups. [ 7.426324] systemd[1]: Reached target Slices. [ OK ] Reached target Slices. [ 7.442270] systemd[1]: Reached target Mounting snaps. [ OK ] Reached target Mounting snaps. [ 7.455791] systemd[1]: Reached target Mounted snaps. [ OK ] Reached target Mounted snaps. [ 7.474384] systemd[1]: Reached target Swap. [ OK ] Reached target Swap. [ 7.531934] systemd[1]: Listening on RPCbind Server Activation Socket. [ OK ] Listening on RPCbind Server Activation Socket. [ 7.558998] systemd[1]: Listening on Syslog Socket. [ OK ] Listening on Syslog Socket. [ 7.574658] systemd[1]: Listening on initctl Compatibility Named Pipe. [ OK ] Listening on initctl Compatibility Named Pipe. [ 7.599269] systemd[1]: Listening on Journal Audit Socket. [ OK ] Listening on Journal Audit Socket. [ 7.622719] systemd[1]: Listening on Journal Socket (/dev/log). [ OK ] Listening on Journal Socket (/dev/log). [ 7.646945] systemd[1]: Listening on Journal Socket. [ OK ] Listening on Journal Socket. [ 7.671028] systemd[1]: Listening on udev Control Socket. [ OK ] Listening on udev Control Socket. [ 7.702694] systemd[1]: Listening on udev Kernel Socket. [ OK ] Listening on udev Kernel Socket. [ 7.766779] systemd[1]: Mounting Huge Pages File System... Mounting Huge Pages File System... [ 7.790971] systemd[1]: Mounting POSIX Message Queue File System... Mounting POSIX Message Queue File System... [ 7.826619] systemd[1]: Mounting RPC Pipe File System... Mounting RPC Pipe File System... [ 7.855178] systemd[1]: Mounting Kernel Debug File System... Mounting Kernel Debug File System... [ 7.882749] systemd[1]: Condition check resulted in Kernel Trace File System being skipped. [ 7.951128] systemd[1]: Starting Journal Service... Starting Journal Service... [ 7.970827] systemd[1]: Condition check resulted in Create list of static device nodes for the current kernel being skipped. [ 7.997111] systemd[1]: Starting Load Kernel Module chromeos_pstore... Starting Load Kernel Module chromeos_pstore... [ 8.030231] systemd[1]: Condition check resulted in Load Kernel Module drm being skipped. [ 8.048770] systemd[1]: Condition check resulted in Load Kernel Module efi_pstore being skipped. [ 8.098814] systemd[1]: Starting Load Kernel Module pstore_blk... Starting Load Kernel Module pstore_blk... [ 8.127707] systemd[1]: Starting Load Kernel Module pstore_zone... Starting Load Kernel Module pstore_zone... [ 8.167035] systemd[1]: Starting Load Kernel Module ramoops... Starting Load Kernel Module ramoops... [ 8.195994] systemd[1]: Condition check resulted in Set Up Additional Binary Formats being skipped. [ 8.254869] systemd[1]: Starting Load Kernel Modules... Starting Load Kernel Modules... [ 8.282940] systemd[1]: Starting Remount Root and Kernel File Systems... Starting Remount Root and Kernel File Systems... [ 8.378978] systemd[1]: Starting udev Coldplug all Devices... Starting udev Coldplug all Devices... [ 8.412546] systemd[1]: Started Journal Service. [ OK ] Started Journal Service. [ OK ] Mounted Huge Pages File System. [ OK ] Mounted POSIX Message Queue File System. [ OK ] Mounted RPC Pipe File System. [ OK ] Mounted Kernel Debug File System. [ OK ] Finished Load Kernel Module chromeos_pstore. [ OK ] Finished Load Kernel Module pstore_blk. [ OK ] Finished Load Kernel Module pstore_zone. [ OK ] Finished Load Kernel Module ramoops. [ OK ] Finished Load Kernel Modules. [ OK ] Finished Remount Root and Kernel File Systems. Mounting Kernel Configuration File System... Starting Flush Journal to Persistent Storage... Starting Load/Save Random Seed... [ 8.710611] systemd-journald[1778]: Received client request to flush runtime journal. Starting Apply Kernel Variables... [ 8.747558] systemd-journald[1778]: File /var/log/journal/b7f0ef9db35a435bb9eda62f4134ae18/system.journal corrupted or uncleanly shut down, renaming and replacing. Starting Create System Users... [ OK ] Mounted Kernel Configuration File System. [ OK ] Finished Load/Save Random Seed. [ OK ] Finished Apply Kernel Variables. [ OK ] Finished Create System Users. Starting Create Static Device Nodes in /dev... [ OK ] Finished Create Static Device Nodes in /dev. [ OK ] Finished Flush Journal to Persistent Storage. [ OK ] Reached target Local File Systems (Pre). [ OK ] Reached target Local File Systems. Starting Preprocess NFS configuration... Starting Tell Plymouth To Write Out Runtime Data... Starting Create Volatile Files and Directories... Starting udev Kernel Device Manager... [ OK ] Finished udev Coldplug all Devices. [ OK ] Finished Preprocess NFS configuration. [ OK ] Finished Tell Plymouth To Write Out Runtime Data. [ OK ] Finished Create Volatile Files and Directories. Starting Helper to synchronize boot up for ifupdown... [ OK ] Reached target NFS client services. Starting RPC bind portmap service... Starting Network Name Resolution... Starting Network Time Synchronization... Starting Update UTMP about System Boot/Shutdown... [ OK ] Started udev Kernel Device Manager. [ OK ] Started RPC bind portmap service. [ OK ] Reached target Remote File Systems (Pre). [ OK ] Reached target Remote File Systems. [ OK ] Reached target RPC Port Mapper. [ OK ] Started Dispatch Password …ts to Console Directory Watch. [ OK ] Reached target Local Encrypted Volumes. [ OK ] Finished Update UTMP about System Boot/Shutdown. [ OK ] Started Network Time Synchronization. [ OK ] Reached target System Initialization. [ OK ] Started CUPS Scheduler. [ OK ] Started Daily Cleanup of Temporary Directories. [ OK ] Started Ubuntu Advantage Timer for running repeated jobs. [ OK ] Reached target Paths. [ OK ] Reached target System Time Set. [ OK ] Reached target System Time Synchronized. [ OK ] Started Trigger anacron every hour. [ OK ] Started Daily apt download activities. [ OK ] Started Daily apt upgrade and clean activities. [ OK ] Started Periodic ext4 Onli…ata Check for All Filesystems. [ OK ] Started Discard unused blocks once a week. [ OK ] Started Refresh fwupd metadata regularly. [ OK ] Started Daily man-db regeneration. [ OK ] Started Message of the Day. [ OK ] Reached target Timers. [ OK ] Listening on Avahi mDNS/DNS-SD Stack Activation Socket. [ OK ] Listening on CUPS Scheduler. [ OK ] Listening on D-Bus System Message Bus Socket. Starting Socket activation for snappy daemon. [ OK ] Started Network Name Resolution. [ OK ] Listening on Socket activation for snappy daemon. [ OK ] Reached target Host and Network Name Lookups. [ OK ] Reached target Sockets. [ OK ] Reached target Basic System. Starting Accounts Service... [ OK ] Started Run anacron jobs. Starting LSB: automatic crash report generation... Starting Avahi mDNS/DNS-SD Stack... [ OK ] Started Regular background program processing daemon. [ OK ] Started CUPS Scheduler. [ OK ] Started D-Bus System Message Bus. Starting Network Manager... [ OK ] Started Save initial kernel messages after boot. Starting Remove Stale Onli…t4 Metadata Check Snapshots... Starting Dispatcher daemon for systemd-networkd... [ OK ] Started Set the CPU Frequency Scaling governor. Starting Authorization Manager... Starting Restore /etc/reso… the ppp link was shut down... Starting System Logging Service... [ OK ] Reached target Login Prompts (Pre). Starting Snap Daemon... Starting Switcheroo Control Proxy service... Starting Login Service... Starting Disk Manager... Starting WPA supplicant... [ OK ] Finished Restore /etc/reso…re the ppp link was shut down. [ OK ] Started System Logging Service. [ OK ] Started WPA supplicant. [ OK ] Started Authorization Manager. [ OK ] Started Accounts Service. [ OK ] Started Network Manager. [ OK ] Started Switcheroo Control Proxy service. [ OK ] Started LSB: automatic crash report generation. [ OK ] Started Avahi mDNS/DNS-SD Stack. Starting Network Manager Wait Online... [ OK ] Started Make remote CUPS printers available locally. Starting Modem Manager... [ OK ] Started Disk Manager. Starting Hostname Service... [ OK ] Started Login Service. [ OK ] Finished Remove Stale Onli…ext4 Metadata Check Snapshots. [ OK ] Started Dispatcher daemon for systemd-networkd. [ OK ] Started Modem Manager. [ OK ] Found device /dev/ttyS2. [ OK ] Started Snap Daemon. Starting Wait until snapd is fully seeded... [ OK ] Started Hostname Service. Starting Time & Date Service... [ 12.168528] brcmfmac: brcmf_fw_alloc_request: using brcm/brcmfmac4356-sdio for chip BCM4356/2 Starting Network Manager Script Dispatcher Service... [ OK ] Found device /sys/subsystem/net/devices/eth0. [ OK ] Started ifup for eth0. [ 12.351837] brcmfmac_wcc: brcmf_wcc_attach: executing [ OK ] Started Network Manager Script Dispatcher Service. [ 12.405297] brcmfmac: brcmf_c_preinit_dcmds: Firmware: BCM4356/2 wl0: Mar 4 2020 22:06:08 version 7.35.349.87 (r724447 CY) FWID 01-c68a2890 [ OK ] Started Time & Date Service. [ OK ] Finished Wait until snapd is fully seeded. [ OK ] Listening on Load/Save RF …itch Status /dev/rfkill Watch. Starting Load/Save RF Kill Switch Status... [ 13.454115] gpio gpiochip0: (gpio0): gpiochip_lock_as_irq: tried to flag a GPIO set as output for IRQ [ 13.470190] gpio gpiochip0: (gpio0): unable to lock HW IRQ 4 for IRQ [ 13.481030] genirq: Failed to request resources for bt_default_wake_host_irq (irq 66) on irqchip rockchip_gpio_irq [ OK ] Started Load/Save RF Kill Switch Status. [ OK ] Found device /sys/subsystem/net/devices/wlan0. [ OK ] Finished Helper to synchronize boot up for ifupdown. [ OK ] Finished Network Manager Wait Online. [ OK ] Started ifup for wlan0. Starting Raise network interfaces... [FAILED] Failed to start Raise network interfaces. See 'systemctl status networking.service' for details. [ OK ] Reached target Network. [ OK ] Reached target Network is Online. Starting Tool to automatic…mit kernel crash signatures... Starting OpenVPN service... Starting OpenBSD Secure Shell server... Starting Permit User Sessions... [ OK ] Started Unattended Upgrades Shutdown. [ OK ] Started crash report submission daemon. [ OK ] Finished OpenVPN service. [ OK ] Finished Permit User Sessions. Starting GNOME Display Manager... Starting Hold until boot process finishes up... [ OK ] Started OpenBSD Secure Shell server. [ OK ] Finished Hold until boot process finishes up. [ OK ] Started Serial Getty on ttyS2. [ OK ] Reached target Login Prompts. [ OK ] Started Tool to automatica…ubmit kernel crash signatures. Ubuntu 20.04.4 LTS rk3399 ttyS2 rk3399 login: root Password: Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 6.3.0 aarch64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage This system has been minimized by removing packages and content that are not required on a system that users do not log into. To restore this content, you can run the 'unminimize' command. Expanded Security Maintenance for Applications is not enabled. 186 updates can be applied immediately. 125 of these updates are standard security updates. To see these additional updates run: apt list --upgradable Enable ESM Apps to receive additional future security updates. See https://ubuntu.com/esm or run: sudo pro status New release '22.04.2 LTS' available. Run 'do-release-upgrade' to upgrade to it. Last login: Wed Jun 14 21:02:11 CST 2023 on ttyS2 root@rk3399:~#
其中rfkill驱动相关信息如下:
[ 3.429513] [BT_RFKILL]: Enter rfkill_rk_init [ 3.434407] [WLAN_RFKILL]: Enter rfkill_wlan_init [ 3.439993] [WLAN_RFKILL]: Enter rfkill_wlan_probe [ 3.445414] [WLAN_RFKILL]: wlan_platdata_parse_dt: wifi_chip_type = ap6356 [ 3.453121] [WLAN_RFKILL]: wlan_platdata_parse_dt: enable wifi power control. [ 3.461114] [WLAN_RFKILL]: wlan_platdata_parse_dt: wifi power controled by gpio. [ 3.469450] [WLAN_RFKILL]: wlan_platdata_parse_dt: WIFI,host_wake_irq = 3, flags = 0. [ 3.478232] [WLAN_RFKILL]: wlan_platdata_parse_dt: The ref_wifi_clk not found ! [ 3.486421] [WLAN_RFKILL]: rfkill_wlan_probe: init gpio [ 3.492276] [WLAN_RFKILL]: rfkill_set_wifi_bt_power: 1 [ 3.498036] [WLAN_RFKILL]: Exit rfkill_wlan_probe [ 3.503936] [BT_RFKILL]: bluetooth_platdata_parse_dt: get property: uart_rts_gpios = 83. [ 3.513055] [BT_RFKILL]: bluetooth_platdata_parse_dt: get property: BT,reset_gpio = 9. [ 3.521946] [BT_RFKILL]: bluetooth_platdata_parse_dt: get property: BT,wake_gpio = 90. [ 3.530847] [BT_RFKILL]: bluetooth_platdata_parse_dt: get property: BT,wake_host_irq = 4. [ 3.540083] [BT_RFKILL]: Request irq for bt wakeup host [ 3.546113] [BT_RFKILL]: ** disable irq [ 3.546158] [BT_RFKILL]: BT_WAKE_HOST IRQ fired [ 3.555645] [BT_RFKILL]: bt shut off power [ 3.586087] [BT_RFKILL]: bt_default device registered.
brcm驱动相关信息如下:
[ 12.064452] cfg80211: Loading compiled-in X.509 certificates for regulatory database [ 12.107359] Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7' [ 12.162831] brcmfmac: F1 signature read @0x18000000=0x17224356 [ 12.168528] brcmfmac: brcmf_fw_alloc_request: using brcm/brcmfmac4356-sdio for chip BCM4356/2 [ 12.351837] brcmfmac_wcc: brcmf_wcc_attach: executing [ 12.405297] brcmfmac: brcmf_c_preinit_dcmds: Firmware: BCM4356/2 wl0: Mar 4 2020 22:06:08 version 7.35.349.87 (r724447 CY) FWID 01-c68a2890
5.2.1 查看网卡
先运行ifconfig查看网络设备信息:
root@rk3399:~# ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.0.101 netmask 255.255.255.0 broadcast 192.168.0.255 ether 92:a7:05:0f:19:86 txqueuelen 1000 (Ethernet) RX packets 114 bytes 10592 (10.5 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 82 bytes 5679 (5.6 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 device interrupt 46 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 loop txqueuelen 1000 (Local Loopback) RX packets 118 bytes 9074 (9.0 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 118 bytes 9074 (9.0 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 wlan0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 ether cc:4b:73:1e:11:26 txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 wlan0:avahi: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 169.254.3.142 netmask 255.255.0.0 broadcast 169.254.255.255 ether cc:4b:73:1e:11:26 txqueuelen 1000 (Ethernet)
可以看到有一个有线网卡ip地址为192.168.0.101,网卡名为eth0;还有一个无线网卡,网卡名为wlan0;
5.2.2 扫描wifi
我们使用nmcli device wifi list命令扫描无线网:
root@rk3399:~# nmcli device wifi list IN-USE BSSID SSID MODE CHAN RATE SIGNAL BARS SECURITY 8C:A6:DF:6F:E4:41 2202 Infra 6 405 Mbit/s 59 ▂▄▆_ WPA1 WPA2
这里扫描到一个SSID为2202的无线网。我们尝试连接它;
root@rk3399:~# nmcli device wifi connect "2202" password "181x1521x112x33" Device 'wlan0' successfully activated with 'c01e0125-76bf-4b6e-873d-b479897a4199'.
我们再次查看网络设备信息,可以发现wlan0的ip地址被设置为了192.168.0.102;
root@rk3399:~# ifconfig wlan0 wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.0.102 netmask 255.255.255.0 broadcast 192.168.0.255 ether cc:4b:73:1e:11:26 txqueuelen 1000 (Ethernet) RX packets 17 bytes 2521 (2.5 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 37 bytes 4638 (4.6 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
5.2.3 网络测试
我们先把有线网卡停掉:
root@rk3399:~# ifconfig eth0 down
然后测试外网:
root@rk3399:~# ping www.baidu.com PING www.a.shifen.com (180.101.50.188) 56(84) bytes of data. 64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=1 ttl=52 time=10.1 ms 64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=2 ttl=52 time=61.4 ms 64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=3 ttl=52 time=22.9 ms 64 bytes from 180.101.50.188 (180.101.50.188): icmp_seq=4 ttl=52 time=14.0 ms ^C --- www.a.shifen.com ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3005ms rtt min/avg/max/mdev = 10.119/27.112/61.413/20.338 ms
可以看到网络是通的,那么我们继续测一下网速。
安装测速工具:
root@rk3399:~# dpkg --configure -a root@rk3399:~# apt update root@rk3399:~# apt install python3-pip root@rk3399:~# pip install speedtest-cli
开始测速:
root@rk3399:~# speedtest-cli Retrieving speedtest.net configuration... Testing from China Telecom (180.114.139.1)... Retrieving speedtest.net server list... Selecting best server based on ping... Hosted by 浙江电信 (NingBo) [257.87 km]: 31.305 ms Testing download speed................................................................................ Download: 16.06 Mbit/s Testing upload speed...................................................................................................... Upload: 6.67 Mbit/s
参考文章
[2] NanoPC-T4开发板原理图
[4] Rockchip基于RK3566/RK3568 WiFi AP6256调试笔记
[5] RK3399驱动开发 | 13 - AP6356 SDIO WiFi 调试(基于linux4.5.104内核)
[7] Linux-MMC子系统
[9] [RK3399] SDIO 接口 Wifi 驱动流程分析 (AP6354)
[10] [物联网篇 ] 15 -博通AP6255模块中WL_HOST_WAKE功能
[11] RK3399驱动开发 | 14 - AP6255 SDIO WiFi 调试(基于linux5.4.32内核)
[12] wifi 驱动开发
[13] 3399验证情况记录