Firefly RK3399 - WIFI AP6356驱动

目录

0 前言

1 相关驱动介绍

1.1 rfkill驱动

1.2 WiFI设备自身驱动

2 设备树以及源码调整

2.1 MMC驱动配置

2.1.1 新增sdio_pwrseq设备节点

 2.1.2 新增wifi_enable_h引脚配置节点

2.1.3 新增sdio0设备节点属性

2.2 WiFi驱动配置

2.2.1 配置brcmfmac驱动

2.2.2 配置IEEE 802.11驱动

2.2.3 固件下载

2.3 rfkill驱动配置

2.3.1 源码拷贝

2.3.2 新增wireless-wlan设备节点

2.3.3 新增wireless-bluetooth设备节点

2.3.4 配置内核

2.4 rk808驱动配置

2.5 配置内核

2.6 编译内核

3 解决错误

 4.烧录测试

4.1 下载内核和WiFi固件

4.2 测试

4.2.1 查看网卡

4.2.2 扫描wifi

4.2.3 网络测试

参考文章 

0 前言

  之前,我成功地为RK3399移植了U-Boot 2023.07、Linux 5.2.8内核以及Ubuntu 20.04.4的根文件系统。为了开发板的网络连接能力,本文章移植WIFI模块AP6356的驱动程序。在此,我将记录下整个学习和移植过程,以便于日后回顾和复习。同时,我也希望这些记录能够为其他正在进行类似工作的学习者提供一定的参考和帮助。

1 相关驱动介绍

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设备自身驱动。

1.1 rfkill驱动

rfkill驱动是和平台SoC有关联的的,一般是SoC厂家提供,这部分的功能是给RF设备(无线通信设备、比如蓝牙、WiFi)上下电使用,比如唤醒引脚配置,当RF设备接收到数据的时候用来唤醒系统,比如上下电引脚配置,用于开启/禁用RF设备,这部分的功能就集成在rfkill-wlan.c、rfkill-bt.c。

1.2 WiFI设备自身驱动

WiFI设备自身驱动一般都是由WiFi芯片厂家提供,比如WiFi芯片厂商瑞昱、博通;这部分是标准的基本不用修改,不管是NXP、Rockchip、全志等平台都是使用这一套代码。

2 设备树以及源码调整

由于linux 5.2.8 版本并没有直接支持AP6356,因此为了支持AP6356这里需要修改的内容比较多,因为这涉及到到了源码以及设备树的调整。

2.1 MMC驱动配置

2.1.1 新增sdio_pwrseq设备节点

修改arch/arm64/boot/dts/rockchip/rk3399-firefly.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 RK_PB2 GPIO_ACTIVE_LOW>;
    };

rk3399-firefly.dts已经有sdio_pwrseq设备节点,无需再修改。

其中属性:

  • 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。

 2.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>;
    };
};

rk3399-firefly.dts已配置,无需再修改。

其中属性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,表示普通配置。

2.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-firefly.dts文件为sdio0设备节点新增属性:

&sdio0 {
    /* WiFi & BT combo module Ampak AP6356S */
    clock-frequency = <150000000>;
    clock-freq-min-max = <200000 150000000>;
    supports-sdio;
    bus-width = <4>;
    disable-wp;
    cap-sdio-irq;
    cap-sd-highspeed;
    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;

    /* Power supply */
    vqmmc-supply = &vcc1v8_s3;  /* IO line */
    vmmc-supply = &vcc_sdio;    /* card's power */
    status = "okay";

};

rk3399-firefly.dts已配置,无需再修改。

2.2 WiFi驱动配置

Rockchip官方平台linux版本(4.4、4.19、5.10)实现了一套兼容多款WiFi芯片的自适应框架,位于drivers/net/wireless/rockchip_wlan路径下,简单来说就是将多款WiFi芯片的驱动编译成xxx.ok文件,比如常用的WiFi芯片厂商瑞昱、博通的WiFi芯片的驱动统一编译成不同的xxx.ko文件。

但是我们使用的Linux 5.2.8 版本并没有drivers/net/wireless/rockchip_wlan这个文件夹,因此我们将这个文件夹从Rockchip官方平台linux版本拷贝过来。

AP6356使用的是博通BCM4356方案,所以在linux中,使能brcmfmac模块驱动即可。

2.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编译成模块。

如果编译进内核,由于加载固件在根文件系统挂载之前,导致出现无法找到固件文件的错误。

2.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编译成模块。

2.2.3 固件下载

固件使用armbian提供的:https://github.com/armbian/firmware/tree/master/brcm

我们把文件全部下载下来,这里面也包含了固件firmware/regulatory.db,下载完成后在内核启动后我们将这些文件拷贝到开发板根文件系统的/lib/目录中。

git clone https://github.com/armbian/firmware.git --depth 1

2.3 rfkill驱动配置

2.3.1 源码拷贝

Rockchip官方linux 4.19版本:net/rfkill/rfkill-bt.c文件复制到linux 5.2.8 net/rfkill路径下;

Rockchip官方linux 5.10版本:net/rfkill/rfkill-wlan.c、rfkill-bt.h、rfkill-wlan.h等文件复制到linux 5.2.8 net/rfkill路径下。

cp /RK3399/linux-4.19/net/rfkill/rfkill-bt.c /RK3399/linux-5.2.8/net/rfkill/

cp /RK3399/linux-5.10/net/rfkill/rfkill-wlan.c /RK3399/linux-5.2.8/net/rfkill/

cp /RK3399/linux-5.10/include/linux/rfkill-bt.h /RK3399/linux-5.2.8/include/linux/

cp /RK3399/linux-5.10/include/linux/rfkill-wlan.h /RK3399/linux-5.2.8/include/linux/

cp /RK3399/linux-5.10/include/linux/wakelock.h /RK3399/linux-5.2.8/include/linux/

cp -R  /RK3399/linux-5.10/include/linux/rockchip /RK3399/linux-5.2.8/include/linux/

cp -R  /RK3399/linux-5.10/include/linux/soc/rockchip /RK3399/linux-5.2.8/include/linux/soc

修改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

2.3.2 新增wireless-wlan设备节点

为了支持rfkill WiFi驱动,在arch/arm64/boot/dts/rockchip/rk3399-firefly.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引脚;

2.3.3 新增wireless-bluetooth设备节点

为了支持rfkill蓝牙驱动,我们需要在arch/arm64/boot/dts/rockchip/rk3399-firefly.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:表示该设备状态为正常运行;
// 这个也是要新增的,默认没有这个配置  在&pinctrl下新增该子节点
wireless-bluetooth {
    uart0_gpios: uart0-gpios {
        rockchip,pins = <2 RK_PC3 RK_FUNC_GPIO &pcfg_pull_none>;
    };
};

2.3.4 配置内核

需要配置内核:

[*] Networking support  --->
     <*> RF switch subsystem support  --->
         <*>   Rockchip RFKILL driver

2.4 rk808驱动配置

在arch/arm64/boot/dts/rockchip/rk3399-firefly.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;
                };
            };
        };
    };
};

rk3399-firefly.dts已配置,无需再修改。

修改&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>;
    };
};

2.5 配置内核

配置完内核之后保存配置为firefly-rk3399_defconfig

存档:

mv firefly-rk3399_defconfig ./arch/arm64/configs/

重新配置内核:

make firefly-rk3399_defconfig

2.6 编译内核

在linux内核根目录下执行如下命令进行编译内核:

make -j8

出现错误!!!

3 解决错误

执行make出现以下错误:

In file included from net/rfkill/rfkill-wlan.c:27:
./include/linux/wakelock.h: 在函数‘wake_lock_destroy’中:
./include/linux/wakelock.h:44:9: 错误: implicit declaration of function ‘wakeup_source_trash’; did you mean ‘wakeup_source_add’? [-Werror=implicit-function-declaration]
44 | wakeup_source_trash(&lock->ws);
| ^~~~~~~~~~~~~~~~~~~
| wakeup_source_add
In file included from net/rfkill/rfkill-bt.c:27:
./include/linux/wakelock.h: 在函数‘wake_lock_destroy’中:
./include/linux/wakelock.h:44:9: 错误: implicit declaration of function ‘wakeup_source_trash’; did you mean ‘wakeup_source_add’? [-Werror=implicit-function-declaration]
44 | wakeup_source_trash(&lock->ws);
| ^~~~~~~~~~~~~~~~~~~
| wakeup_source_add

不存在wakeup_source_trash函数,也就是linux 5.2.8 内核中没有这个函数,但是我们从Rockchip官方linux 5.10版本拷贝的rfkill-wlan.h文件使用到这个函数,没有我们就添加。在查看官方linux 5.10 源码后发现,wakeup_source_trash函数定义在include/inux/pm_wakeup.h,函数的原型为:

static inline void wakeup_source_trash(struct wakeup_source *ws)
{
    wakeup_source_remove(ws);
    wakeup_source_drop(ws);
}

在linux 5.2.8 的源码也在相应文件上添加这个函数。

继续make,继续报错!!!我们添加的wakeup_source_trash函数中使用到wakeup_source_drop函数,linux 5.2.8 的源码没有wakeup_source_drop函数,没有我们就继续添加,在文件include/inux/pm_wakeup.h中添加声明:

static inline void wakeup_source_drop(struct wakeup_source *ws) {}

extern void wakeup_source_drop(struct wakeup_source *ws);

在文件drivers/base/power/wakeip.c中添加定义:

/**
 * wakeup_source_drop - Prepare a struct wakeup_source object for destruction.
 * @ws: Wakeup source to prepare for destruction.
 *
 * Callers must ensure that __pm_stay_awake() or __pm_wakeup_event() will never
 * be run in parallel with this function for the same wakeup source object.
 */
void wakeup_source_drop(struct wakeup_source *ws)
{
    if (!ws)
        return;

    __pm_relax(ws);
}
EXPORT_SYMBOL_GPL(wakeup_source_drop);

继续make,继续报错!!!出现以下错误:

aarch64-none-linux-gnu-ld: warning: .tmp_vmlinux1 has a LOAD segment with RWX permissions
aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `rockchip_wifi_set_carddetect':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:408:(.text+0x514): undefined reference to `mmc_host_rescan'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:408:(.text+0x514): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `mmc_host_rescan'
aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `rockchip_wifi_power':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0xfc): undefined reference to `primary_sdio_host'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0xfc): relocation truncated to fit: R_AARCH64_ADR_PREL_PG_HI21 against undefined symbol `primary_sdio_host'
aarch64-none-linux-gnu-ld: /RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0x100): undefined reference to `primary_sdio_host'
aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `get_wifi_addr_vendor':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:484:(.text.unlikely+0xbac): undefined reference to `is_rk_vendor_ready'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:484:(.text.unlikely+0xbac): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `is_rk_vendor_ready'
aarch64-none-linux-gnu-ld: /RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:489:(.text.unlikely+0xbd4): undefined reference to `rk_vendor_read'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:489:(.text.unlikely+0xbd4): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `rk_vendor_read'
make: *** [Makefile:1055:vmlinux] 错误 1

没有rk_vendor_read、is_rk_vendor_ready、get_wifi_addr_vendor等函数,发现这些函数定义在rk_vendor_storage.c文件中,而在linux 5.2.8中并没有这个文件,所以:

sudo cp ../kernel-5.10/drivers/soc/rockchip/rk_vendor_storage.c ./drivers/soc/rockchip/

并修改/drivers/soc/rockchip/目录下的Makefile添加:

obj-y += rk_vendor_storage.o

继续make,继续报错!!!出现以下错误:

aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `rockchip_wifi_set_carddetect':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:408:(.text+0x514): undefined reference to `mmc_host_rescan'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:408:(.text+0x514): relocation truncated to fit: R_AARCH64_CALL26 against undefined symbol `mmc_host_rescan'
aarch64-none-linux-gnu-ld: net/rfkill/rfkill-wlan.o: in function `rockchip_wifi_power':
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0xfc): undefined reference to `primary_sdio_host'
/RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0xfc): relocation truncated to fit: R_AARCH64_ADR_PREL_PG_HI21 against undefined symbol `primary_sdio_host'
aarch64-none-linux-gnu-ld: /RK3399/linux-5.2.8/net/rfkill/rfkill-wlan.c:312:(.text.unlikely+0x100): undefined reference to `primary_sdio_host'
make: *** [Makefile:1055:vmlinux] 错误 1

不存在mmc_host_rescan函数,linux 5.2.8内核中没有这个函数,该函数定义在drivers/mmc/core/host.c文件,原型为:

/**
 * mmc_host_rescan - triger software rescan flow
 * @host: mmc host
 *
 * rescan slot attach in the assigned host.
 * If @host is NULL, default rescan primary_sdio_host
 * saved by mmc_add_host().
 * OR, rescan host from argument.
 *
 */
int mmc_host_rescan(struct mmc_host *host, int val, int is_cap_sdio_irq)
{
	if (NULL != primary_sdio_host) {
		if (!host)
			  host = primary_sdio_host;
		else
			pr_info("%s: mmc_host_rescan pass in host from argument!\n",
				mmc_hostname(host));
	} else {
		pr_err("sdio: host isn't  initialization successfully.\n");
		return -ENOMEDIUM;
	}

	pr_info("%s:mmc host rescan start!\n", mmc_hostname(host));

	/*  0: oob  1:cap-sdio-irq */
	if (is_cap_sdio_irq == 1) {
		host->caps |= MMC_CAP_SDIO_IRQ;
	} else if (is_cap_sdio_irq == 0) {
		host->caps &= ~MMC_CAP_SDIO_IRQ;
	} else {
		dev_err(&host->class_dev, "sdio: host doesn't identify oob or sdio_irq mode!\n");
		return -ENOMEDIUM;
	}

	if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->set_sdio_status)
		host->ops->set_sdio_status(host, val);

	return 0;
}
EXPORT_SYMBOL(mmc_host_rescan);

因为linux 5.2.8 没有 host->ops->set_sdio_status,为了不修改过多造成更多的错误,我就将它删除if,等移植后看看有没有问题,没有问题就不管,主打一个能跑就行。

if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->set_sdio_status)
        host->ops->set_sdio_status(host, val);

在linux-5.2.8目录下drivers/mmc/core/host.c文件添加以下内容:

/**
 * mmc_host_rescan - triger software rescan flow
 * @host: mmc host
 *
 * rescan slot attach in the assigned host.
 * If @host is NULL, default rescan primary_sdio_host
 * saved by mmc_add_host().
 * OR, rescan host from argument.
 *
 */
int mmc_host_rescan(struct mmc_host *host, int val, int is_cap_sdio_irq)
{
    if (NULL != primary_sdio_host) {
        if (!host)
              host = primary_sdio_host;
        else
            pr_info("%s: mmc_host_rescan pass in host from argument!\n",
                mmc_hostname(host));
    } else {
        pr_err("sdio: host isn't  initialization successfully.\n");
        return -ENOMEDIUM;
    }

    pr_info("%s:mmc host rescan start!\n", mmc_hostname(host));

    /*  0: oob  1:cap-sdio-irq */
    if (is_cap_sdio_irq == 1) {
        host->caps |= MMC_CAP_SDIO_IRQ;
    } else if (is_cap_sdio_irq == 0) {
        host->caps &= ~MMC_CAP_SDIO_IRQ;
    } else {
        dev_err(&host->class_dev, "sdio: host doesn't identify oob or sdio_irq mode!\n");
        return -ENOMEDIUM;
    }
    return 0;
}
EXPORT_SYMBOL(mmc_host_rescan);

mmc_host_rescan函数中有使用到primary_sdio_host,linux 5.2.8 内核并没有定义,添加定义:

struct mmc_host *primary_sdio_host;

对比了Rockchip官方linux 5.10版本,primary_sdio_host在mmc_add_host函数下有使用到:

 if (host->restrict_caps & RESTRICT_CARD_TYPE_SDIO)
        primary_sdio_host = host;

因为linux 5.2.8 没有定义host->restrict_caps和RESTRICT_CARD_TYPE_SDIO,为了不改动过多,我就将它删除if,直接添加到mmc_add_host函数里:

primary_sdio_host = host;

继续make,没有报错,至此编译通过,成功生成Image

4 烧录测试

4.1 下载内核和WiFi固件

通过上面的流程我们得到:

boot.img

linux-5.2.8/firmware

linux-5.2.8/modules.builtin

linux-5.2.8/modules.order

linux-5.2.8/net/wireless/cfg80211.ko

linux-5.2.8/drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko

linux-5.2.8/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko

将内核镜像boot.img烧录到eMMC第0x8000个扇区处,启动板子,启动内核。

我们将内核编译出的模块打包放到根文件系统/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;

将以上文件拷贝到:

mkdir -p /lib/modules/5.2.8

可以使用多种方式拷贝

firmware --> 开发板/lib

modules.builtin --> 开发板/lib/modules/5.2.8

modules.order --> 开发板/lib/modules/5.2.8

cfg80211.ko --> 开发板/lib/modules/5.2.8

brcmutil.ko --> 开发板/lib/modules/5.2.8

brcmfmac.ko --> 开发板/lib/modules/5.2.8

内核启动的时候,使用/sbin/modprobe加载/lib/modules/{uname -r命令查看}下面的模块,modules.order文件指定了模块的加载顺序。需要在加载模块之前建立该模块的依赖关系,也即必须用depmod来更新一下/lib/modules/$(uname -r)/modules.dep 文件:

在开发板/lib/modules/5.2.8下执行:

depmod -a

4.2 测试

4.2.1 查看网卡

先运行ifconfig查看网络设备信息:

eth0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether 8e:3c:ec:3a:60:82  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
        device interrupt 27

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 173  bytes 13727 (13.7 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 173  bytes 13727 (13.7 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether d4:12:43:82:2c:2c  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。

4.2.2 扫描wifi

我们使用nmcli device wifi list命令扫描无线网:

IN-USE  BSSID              SSID        MODE   CHAN  RATE        SIGNAL  BARS  SE
CURITY
        C6:44:44:0E:70:51  K40         Infra  11
    270 Mbit/s  39      ▂▄__  WPA2
        7E:36:BD:87:55:2B  NK          Infra  11
    130 Mbit/s  37      ▂▄__  WPA2
        F8:32:E4:D6:07:B0  nb          Infra  1
    270 Mbit/s  25      ▂___  WPA2
        FA:32:E4:D6:07:B0  nb2         Infra  14
9   270 Mbit/s  24      ▂___  WPA2

这里扫描到一个SSID为K40的无线网。我们尝试连接它:

nmcli device wifi connect "K40" password "1234567890"

我们再次查看网络设备信息:

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::24e4:6ddb:bd69:98f1  prefixlen 64  scopeid 0x20<link>
        inet6 2408:8459:6a10:23e0:9aa8:69ac:9049:756a  prefixlen 64  scopeid 0x0<global>
        inet6 2408:8459:6a10:23e0:6cad:9b9:3d7d:ee87  prefixlen 64  scopeid 0x0<global>
        ether d4:12:43:82:2c:2c  txqueuelen 1000  (Ethernet)
        RX packets 52  bytes 6974 (6.9 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 115  bytes 17264 (17.2 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

4.2.3 网络测试

然后测试连接互联网:

ping www.baidu.com

可以看到网络是通的:

PING www.baidu.com(2408:8756:c52:1107:0:ff:b035:844b (2408:8756:c52:1107:0:ff:b035:844b)) 56 data bytes
64 bytes from 2408:8756:c52:1107:0:ff:b035:844b (2408:8756:c52:1107:0:ff:b035:844b): icmp_seq=1 ttl=51 time=98.9 ms
64 bytes from 2408:8756:c52:1107:0:ff:b035:844b (2408:8756:c52:1107:0:ff:b035:844b): icmp_seq=2 ttl=51 time=140 ms
64 bytes from 2408:8756:c52:1107:0:ff:b035:844b (2408:8756:c52:1107:0:ff:b035:844b): icmp_seq=3 ttl=51 time=398 ms
^C
--- www.baidu.com ping statistics ---
4 packets transmitted, 3 received, 25% packet loss, time 3005ms
rtt min/avg/max/mdev = 98.857/212.139/397.593/132.205 ms

参考文章 

Rockchip RK3399 - WiFi AP6356驱动 - 大奥特曼打小怪兽 - 博客园 (cnblogs.com)

S3C2440上 MMC/SD卡驱动实例开发讲解

NanoPC-T4开发板原理图

RK3399教程:wifi驱动调试技巧

Rockchip基于RK3566/RK3568 WiFi AP6256调试笔记

RK3399驱动开发 | 13 - AP6356 SDIO WiFi 调试(基于linux4.5.104内核)

Linux SD卡/SDIO驱动开发0-基本知识

Linux-MMC子系统

RK3399 GPIO 配置与使用

[RK3399] SDIO 接口 Wifi 驱动流程分析 (AP6354)

[物联网篇 ] 15 -博通AP6255模块中WL_HOST_WAKE功能

RK3399驱动开发 | 14 - AP6255 SDIO WiFi 调试(基于linux5.4.32内核)

wifi 驱动开发

3399验证情况记录

wifi/BT芯片RTL8723DS驱动移植和测试之一wifi篇

  • 39
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值