编译openwrt

nxp openwrt 编译指南
万字讲解OpenWrt防火墙iptables,并使用UCI配置防火墙
iptables/netfilter 详解
Linux下iptables防火墙配置
iptables&Netfilter简介

交叉编译工具链设置

#!/bin/bash
export TOOLCHAINS_ROOT=/home/zjh/zjh/toolchains/gcc-linaro-7.3.1-2018.05-x86_64_aarch64-linux-gnu
export LD_LIBRARY_PATH=$TOOLCHAINS_ROOT/lib:$LD_LIBRARY_PATH
export PATH=$TOOLCHAINS_ROOT/bin/:$PATH
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

uboot 添加usb

在这里插入图片描述
在uboot 中添加usb功能,除了menuconfig 和设备树要配置,还需要注意引脚的配置。
查看USB 控制器信号,USB1_VBUS 负责USB控制器的供电,一般会供给一个5V电压;USB1_DRVVBUS 是供电使能,它是一个输出信号,一般会连接到外部的mos管或其它使能芯片等等,当它输出高电平时,将会使能供给USB1_VBUS 的5V 电源,因此USB 控制器得到供电。
在ls1046中,这个引脚要配置成gpio,输出高电平,只有当USB1_DRVVBUS输出高电平时USB1/2/3 的USB1_VBUS 引脚才能有5V电压。
gpio输出高电平的代码可以在板级文件中添加 “board/freescale/ls1046ardb/ls1046ardb.c” 。
USB1 的USB_DRVVBUS 和 USB_PWRFAULT 在RCW字段USB_DRVVBUS、USB_PWRFAULT 复用;USB2 和3的这两个信号脚需要在CCSR 的SCFG 的SCFG_RCWPMUXCR0 寄存器中配置。
我们平时看见的外设控制器寄存器,也属于CCSR。
在这里插入图片描述
在这里插入图片描述

USB2、USB3 的DRVVBUS和 PWRFAULT 引脚模式修改:
手册参考ls1046arm.pdf 寄存器Extended RCW PinMux Control Register (SCFG_RCWPMUXCR0)
可以复用为下面集中模式:
在这里插入图片描述
在这里插入图片描述
uboot 代码修改:
board/freescale/ls1046ardb/ls1046ardb.c

void config_board_mux(void)
{
#ifdef CONFIG_HAS_FSL_XHCI_USB
        struct ccsr_scfg *scfg = (struct ccsr_scfg *)CONFIG_SYS_FSL_SCFG_ADDR;
        u32 usb_pwrfault;

        //设置rgmii1 使用rgmii2 的参考时钟      
        out_be32(&scfg->ecgtxcmcr, 0x8000000);
        /* USB2 和USB3 的power由USB1_DRVVBUS(复用作gpio)控制,USB2、3 的power事能和故障检测被复用为I2C*/
        out_be32(&scfg->rcwpmuxcr0, 0);
        out_be32(&scfg->usbdrvvbus_selcr, SCFG_USBDRVVBUS_SELCR_USB1);
        usb_pwrfault = (SCFG_USBPWRFAULT_DEDICATED <<
                        SCFG_USBPWRFAULT_USB3_SHIFT) |
                        (SCFG_USBPWRFAULT_DEDICATED <<
                        SCFG_USBPWRFAULT_USB2_SHIFT) |
                        (SCFG_USBPWRFAULT_SHARED <<
                        SCFG_USBPWRFAULT_USB1_SHIFT);
        out_be32(&scfg->usbpwrfault_selcr, usb_pwrfault);
#endif
}

#ifdef CONFIG_MISC_INIT_R
int misc_init_r(void)
{
        config_board_mux();
        return 0;
}
#endif

linux 启动

LS 系列使用文档
LS1046A 调试记录
LS1043 DDR 验证

  1. PBL/RCW
    ls1046 启动uboot,首先要从外部flash 读取PBL/RCW(Perboot Load,预引导)程序。
    PBL 包括以下配置:
    • 引脚复用、SerDes 协议选择。
    • 时钟参数和PLL 倍数选择。
    • 包含要运行的第一个软件(不在内部BootROM中)的设备。
  2. 运行内部BootRom 存储的程序并配置SOC 底层方面。
  3. BootRom 从emmc 加载第一个外部程序 TF-A (初始化ddr)
    • BootROM将控制传递给BL2。
    • BL2 从emmc 加载bootloader 并开始bootloader。
      ls1046 启动过程

RCW 配置

ls1046 sd/emmc rcw 配置文件:packages/firmware/rcw/ls1046ardb/RR_FFSSPPPH_1133_5559/rcw_1800_sdboot.rcw
根据参考手册LS1046ARM.pdf 第4章 Reset, Clocking, and Initialization,Reset configuration word (RCW) 配置。

ls系列的许多硬件控制都放在RCW 中,比如cpu core时钟、Platform 时钟、DDR 时钟、Serdes 协议和时钟和引脚复用等等。
ls1046启动的第一步就是加载RCW 配置,如果你是在你自己设计的板子上启动,对于一些关键的配置,需要修改后才能启动。(时钟配置错误无法启动的现象就是没有任何打印

  1. 时钟的配置
    系统时钟、DDR时钟在ls1046a 的datasheet,5.1 Clock ranges章节给出以下表格:
    根据你的cpu核的速率选择( LS1046A 拥有4个cpu核,都是 Cortex®-v8 A72,最高速率可达到1.8Ghz),可以偏低一些选择,最高配置可能无法启动。
    在这里插入图片描述
    以下时钟描述来自LS1046ARM.pdf 的4.4.7 Clocking 章节。
    单一的板上振荡源会提供100MHz 参考时钟给下列PLLs:
    SerDes PLLs 需要另外提供4个参考时钟。
    注意:在SerDes 参考时钟没有提供并且RCW中RCW SRDS_PLL_PD_S1 和RCW SRDS_PLL_PD_S2 字段 使能SerDes 时钟时,芯片将不会完成复位。(因此当RCW 中SerDes时钟配置错误的情况下是无法启动的,调试启动阶段时可以使用默认配置,防止配置错误影响启动)
    在这里插入图片描述
    可以通过IFC_WE0_B 选择SYSCLK 或DIFF_SYSCLK 作为单源振荡器
    在这里插入图片描述

下图展示了ls1046 的时钟结构:
外部时钟源:
首先看到的是Onboard oscillator 100 MHz,这是由核心板上提供的多路差分信号 时钟源,分别有DIFF_SYSCLK_B/DIFF_SYSCLK、SD1_REF_CLK1_P/SD1_REF_CLK1_N等4 路。
另外还有非差分信号时钟SYSCLK、EC1_GTX_CLK125 和EC2_GTX_CLK125,它们也是由核心板上的晶振源提供的。

时钟复用:
SYS_REF_CLK 可以通过复用器cfg_eng_use0 选择是使用SYSCLK 或是DIFF_SYSCLK_B/DIFF_SYSCLK,提供给Core PLL和Platform PLL。
DDR PLL 可以通过RCW[DDR_REFCLK_SEL] 字段选择DDRCLK或DIFF_SYSCLK_B/DIFF_SYSCLK 作为时钟源,并通过RCW[MEM_PLL_RAT]、RCW[MEM_PLL_CFG]、RCW[MEM_PLL_SPD] 选择时钟速率。

SD1_REF_CLK1_P/SD1_REF_CLK1_N 等4路时钟 分别提供给4个SerDes PLL。
在这里插入图片描述
核心板上的时钟提供情况
在这里插入图片描述
Core cluster group PLL 决定了cpu的运行频率,它是多少Hz cpu0~3 就是多少Hz。
在这里插入图片描述
下图展示了Core cluster group PLL 的配置流程:
首先由SYSCLK 和另外两个时钟选择复用,提供给CGA_PLL1 和CGA_PLL2作为时钟源,RCW[CGA_PLL1_RAT] 可以对100MHz的时钟源翻倍 1: n,得到n*100MHz 的CGA_PLL1 和CGA_PLL2,最后由复用器C1选择CGA_PLL1、CGA_PLL1/2、CGA_PLL2 或CGA_PLL2/2 其中之一作为Core cluster group PLL。

在这里插入图片描述
其它外设模块时钟:从图4-6 可以看出Platform PLL 主要时供给外设使用的。
在这里插入图片描述
SerDes 时钟配置
SerDes 设置不同协议时,要求配置的时钟也不同(参考手册)。在调试启动阶段时,可以使用默认的协议与时钟配置,保证能启动,用到功能时再调试SerDes。
SerDes 协议可以用RCW[SRDS_PRTCL_S1] 和RCW[SRDS_PRTCL_S2] 选择,协议参考 31.1.2 SerDes protocols。
我的配置是 0x1040-0x5a59,SD1 laneD用作XFI,laneB 用作QSGMII。SD2 laneA 和laneC使用pcie、laneB 使用SGMII、laneD 使用SATA。
在这里插入图片描述
在这里插入图片描述
根据外部提供的晶振配置 RCW[SRDS_REFCLK_SEL_S1]、RCW[SRDS_REFCLK_SEL_S2] 选择PLL2 的时钟源,这里主要是给核心板上没有提供SD1_REF_CLK2_P/SD1_REF_CLK2_N、SD2_REF_CLK2_P/SD2_REF_CLK2_N 时钟的情况下配置的,如果只有PLL1 那就选择PLL1。
在这里插入图片描述
在这里插入图片描述
RCW[SRDS_PLL_PD_S1] 和RCW[SRDS_PLL_PD_S2] 可以分别使能和关闭SerDes1、2 的PLL1、2,在不使用的情况下可以关闭。

RCW[SRDS_PLL_REF_CLK_SEL_S1] 和 RCW[SRDS_PLL_REF_CLK_SEL_S2] 可以选择SerDes1、2 —PLL1、2 参考时钟的频率。
如图可知当SerDes 工作在不同的协议,要求的时钟频率也是不一样的(参考章节 31.2 SerDes clocking,表31-4)
在这里插入图片描述
从表31-4 可以看出大部分协议都是兼容100MHz 和125MHz,SRDS_PLL_REF_CLK_SEL_S1/2 PLL1/2 设置0 或 1应该都能正常工作。需要注意的是XFI 只支持156.25M,使用在使用XFI 的时候要将SRDS_PLL_REF_CLK_SEL_S1 PLL2 配置成1.
在这里插入图片描述
另外使用PCIE 的时候,根据PCIE 的速度配置SRDS_DIV_PEX_S1 和SRDS_DIV_PEX_S2
在这里插入图片描述
2. 引脚配置
除了时钟配置之外,还要注意其它一些启动过程中所需要外设的引脚配置情况。
比如EMMC,关于EMMC 的描述在ls1046ARM.pdf 第19 章节eSDHC。
这是一个sd 设备控制器,主要支持以下设备:
我的板子使用的是4 bit 的MMC(emmc)
在这里插入图片描述
在这里插入图片描述
eSDHC 的信号线主要以下几个
在这里插入图片描述
根据原理图我们需要配置SDHC_DAT0~3、SOC_SD_CLK和SDHC_CMD 引脚;因为用的是4bit SDHC_DAT4-7 可以配成SPI。这里特别要注意的是SDHC_CD_B 和SDHC_WP 信号,SDHC_CD_B 用来检测sd 设备是否插入,当你不使用这个引脚时一定要将它配成其它模式,否者控制器会检测不到sd 设备导致emmc 初始化失败(打开debug 后会提示:esdhc_emmc_init error: cins not set in prsstat)。

上述几个引脚可以通过RCW[SDHC_BASE] 和RCW[IIC2_EXT] 配置。

  1. LVDD、TVDD、DVDD 和EVDD 电平选择
    这个主要时配置一些引脚1.8V、2.5V 等等,可以通过RCW[LVDD_VSEL]、RCW[TVDD_VSEL]、RCW[DVDD_VSEL] 和RCW[EVDD_VSEL] 选择。

根据上面的描述,我的RCW 配置是:
注意:文件中用的10进制,手册中用的16进制。

SYS_PLL_RAT=4
MEM_PLL_RAT=13
CGA_PLL1_RAT=16
CGA_PLL2_RAT=10
SRDS_PRTCL_S1=4160
SRDS_PRTCL_S2=23129
SRDS_PLL_REF_CLK_SEL_S1=1		//RCW位的顺序是反(0~511)的,不能按正常来算。
SRDS_PLL_REF_CLK_SEL_S2=0
SRDS_DIV_PEX_S1=1
SRDS_DIV_PEX_S2=1
DDR_FDBK_MULT=2
DDR_REFCLK_SEL=1
PBI_SRC=6
IFC_MODE=64
HWA_CGA_M1_CLK_SEL=6
DRAM_LAT=1
SPI_EXT=0
SPI_BASE=0
SDHC_EXT=0
SDHC_BASE=0
UART_BASE=7
IFC_GRP_A_EXT=1
IFC_GRP_D_EXT=1
IFC_GRP_E1_EXT=1
IFC_GRP_F_EXT=1
IRQ_OUT=1
LVDD_VSEL=0
TVDD_VSEL=1
DVDD_VSEL=2
EVDD_VSEL=0
IIC2_EXT=0
SYSCLK_FREQ=600
HWA_CGA_M2_CLK_SEL=1

DDR 参数修改

DDR 参数配置
ls系列DDR 参数配置文件:<atf_dir>/plat/nxp/soc-/ardb/ddr_init.c
各种DDR 控制器驱动文件夹:<atf_dir>/plat/nxp/drivers/ddr
除了参考文章里添加的宏外,如果你使用的是DDR 颗粒还需要在plat/nxp/drivers/ddr/nxp-ddr/ddr.h 文件中添加以下定义:
#define CONFIG_STATIC_DDR
如果你想查看更多的 DDR 打印信息,可以添加宏定义:
#define DDR_DEBUG

bootcmd 解析

bootcmd 调用 distro_bootcmd:

bootcmd=run distro_bootcmd;run sd_bootcmd; env exists secureboot && esbc_halt;

Distro Bootcmd 是U-Boot中设计的一种启动机制,用来自适应各种不同的启动媒介,并从中找到可用的启动镜像然后启动。

distro_bootcmd=scsi_need_init=; setenv nvme_need_init; for target in ${boot_targets}; do run bootcmd_${target}; done

distro_bootcmd 中查找各种启动媒介boot_targets,并执行bootcmd_${target},即依次执行bootcmd_scsi0、bootcmd_mmc0 等等。

boot_targets=scsi0 mmc0 usb0 dhcp

scsi 不太熟悉,直接从mmc 开始查看,mmc 即emmc 或sd 卡设备。
设置启动的设备号devnum=0,这个需要根据soc外接出来的SDHC 接口来定,比如imx6ull 有两个SDHC 接口,那么它可以接两个mmc 设备,mmc0 或mmc1。ls1046 只有一个SDHC 接口所以只能是0。

bootcmd_mmc0=devnum=0; run mmc_boot

切换到要启动的mmc设备,设置devtype=mmc,执行scan_dev_for_boot_part。

mmc_boot=if mmc dev ${devnum}; then devtype=mmc; run scan_dev_for_boot_part; fi

part list 列出mmc 的所有分区,并将分区号存放到devplist。
env exists devplist 判断环境变量devplist 是否存在,不存在的话设置为1。
for distro_bootpart in ${devplist}; 依次读取分区号到distro_bootpart。
fstype ${devtype} d e v n u m : {devnum}: devnum:{distro_bootpart} bootfstype 判断分区的文件系统,并存放到bootfstype 。mmc 设备一般做2个分区,1和2,分区1 是fat 文件系统,用来存放内核与设备树;分区2 一般是ext3、ext4,用来存放rootfs。
执行scan_dev_for_boot。

scan_dev_for_boot_part=part list ${devtype} ${devnum} devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done
#依次读取boot_prefixes,存放到prefix,boot_prefixes 的值是/ 和/boot,启动文件可能存放在这两个文件夹中。
#执行scan_dev_for_extlinux。
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;run scan_dev_for_efi;

boot_prefixes=/ /boot/
#scan_dev_for_extlinux 判断分区中${prefix}${boot_syslinux_conf} 文件是否存在,就是“/extlinux/extlinux.conf” 或 #“/boot/extlinux/extlinux.conf”,ext 表示外部,此文件存放了内核与设备树等的位置。
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${boot_syslinux_conf}; then echo Found ${prefix}${boot_syslinux_conf}; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi

boot_syslinux_conf=extlinux/extlinux.conf
#执行 boot_extlinux。
#sysboot 从mmc 中读取${prefix}${boot_syslinux_conf}文件到 内存。
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}${boot_syslinux_conf}

#从上面可以得知scan_dev_for_extlinux 就是加载extlinux.conf 文件到内存中。
#接着看scan_dev_for_scripts
#scan_dev_for_scripts 判断ls1046ardb_boot.scr 文件是否存在,然后执行boot_a_script
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done

boot_scripts=ls1046ardb_boot.scr
#load 从mmc 加载脚本文件到内存,如果secureboot 存在还需要加载另外两个脚本。
#最后source ls1046ardb_boot.scr,执行这个启动脚本
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; env exists secureboot && load ${devtype} ${devnum}:${distro_bootpart} ${scripthdraddr} ${prefix}${boot_script_hdr}; env exists secureboot && esbc_validate ${scripthdraddr};source ${scriptaddr}

extlinux.conf 文件示例(ls1046 sdk中不支持这个文件):
说明了内核与设备树在flash 中存放的位置。

label rockpi4b+ archlinuxarm
    kernel /boot/Image
    fdt /boot/dtbs/rk3399-rock-pi-4b-plus.dtb
	append earlycon=uart8250,mmio32,0xff1a0000 swiotlb=1 coherent_pool=1m earlyprintk console=ttyS2,1500000n8 rw root=/dev/mmcblk1p1 rootfstype=ext4 rootwait

boot.scr 是一个启动脚本,用它来设置启动方式时就比较方便
ls1046ardb_boot.scr 文件存放在flexbuild_lsdk2012/build/firmware/u-boot/ls1046ardb/ls1046ardb_boot.scr
ls主要内容如下(文件开头有一段乱码(校验码)不用在意):
设置secureboot_validate;
设置dtb 设备树文件名,设置kernel_image 内核文件名。
判断boot分区变量devpart_boot 是否存在,不存在设置为2(我们的boot分区要设置为1) 。
判断rootfs 分区变量devpart_root ,我们要设为2。
part uuid 记录分区uuid 到partuuidr。
设置bootargs 启动参数。
**load 内核文件到地址kernel_addr_r **
**load dtb 文件到fdt_addr_r **
启动内核 booti $kernel_addr_r - $fdt_addr_r

setenv secureboot_validate 'load $devtype $devnum:$devpart_boot $kernelheader_addr_r /secboot_hdrs/ls1046ardb/hdr_linux.out; load $devtype $devnum:$devpart_boot $fdtheader_addr_r /secboot_hdrs/ls1046ardb/hdr_dtb.out; esbc_validate $kernelheader_addr_r; esbc_validate $fdtheader_addr_r'
env exists dtb || setenv dtb fsl-ls1046a-rdb-sdk.dtb;env exists kernel_image || setenv kernel_image Image;env exists devpart_boot || setenv devpart_boot 2;env exists devpart_root || setenv devpart_root 4;part uuid $devtype $devnum:$devpart_root partuuidr;setenv bootargs console=ttyS0,115200 earlycon=uart8250,mmio,0x21c0500 root=PARTUUID=$partuuidr rw rootwait $othbootargs;load $devtype $devnum:$devpart_boot $kernel_addr_r $kernel_image;load $devtype $devnum:$devpart_boot $fdt_addr_r $dtb;env exists secureboot && echo validating secureboot && run secureboot_validate;booti $kernel_addr_r - $fdt_addr_r

从上面的分析可以得知mmc 启动方式是执行 ls1046ardb_boot.scr 脚本完成的。

接着看从usb 启动:
在usb_boot 中也调用了scan_dev_for_boot_part,后面的启动步骤和mmc 是相同的。

=> printenv bootcmd_usb0
bootcmd_usb0=devnum=0; run usb_boot
=> printenv usb_boot
usb_boot=usb start; if usb dev ${devnum}; then devtype=usb; run scan_dev_for_boot_part; fi

接着看从网络启动:bootcmd_dhcp

bootcmd_dhcp=run boot_net_usb_start; run boot_pci_enum; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00011:UNDI:003000;setenv bootp_arch 0xb;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdtaddr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci;

#启动usb
boot_net_usb_start=usb start
#枚举 pci 设备
boot_pci_enum=pci enum
#加载dhcp 脚本到内存地址
dhcp ${scriptaddr} ${boot_script_dhcp}
	scriptaddr=0x80000000
	boot_script_dhcp=boot.scr.uimg
#如果加载成功的话,执行脚本
source ${scriptaddr}
#设置设备树文件名
setenv efi_fdtfile ${fdtfile};

ls1046 uboot 网口配置

ls1046 一共有8个mac(1~6 支持1G,9-10同时支持1G和10G),其中2个支持RGMII 接口,6个SGMII 接口(其中4个可以支持QSGMII接口)。
SGMII 接口和QSGMII 接口的信号线和 SerDes 一样,因此它们被配置到SerDes 复用里,要使用SGMII \QSGMII 先要配置SerDes 的协议。
RGMII 不用配置SerDes 协议。
根据你的底板所用的功能来配置SerDes 协议,我的配置是0x1040-0x5a69。(SerDes 协议其它细节在上面的RCW 配置章节)

phy 地址配置

ls1046 的phy地址配置在 packages/firmware/u-boot/board/freescale/ls1046ardb/eth.c 。
ls1046 一共有两条mdio总线,你需要根据原理图的设计来查看哪一条mdio 总线分别管理了哪些phy 设备,并且phy 设备和哪个mac 相连,程序中按照原理图来配置。

fm_memac_mdio_init:注册mdio 总线。
fm_info_set_phy_address:设置于mac 对应phy的地址。
fm_info_set_mdio:绑定mdio 总线和mac 设备。
注意:下面代码中注册mdio 时两条mdio的reg值要互换一下,否则就找不到phy设备。

int board_eth_init(bd_t *bis)
{
#ifdef CONFIG_FMAN_ENET
        int i;
        struct memac_mdio_info dtsec_mdio_info;
        struct memac_mdio_info tgec_mdio_info;
        struct mii_dev *dev;
        u32 srds_s1;
        struct ccsr_gur *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);

        srds_s1 = in_be32(&gur->rcwsr[4]) &
                        FSL_CHASSIS2_RCWSR4_SRDS1_PRTCL_MASK;
        srds_s1 >>= FSL_CHASSIS2_RCWSR4_SRDS1_PRTCL_SHIFT;

        /* mdio 总线注册*/
        dtsec_mdio_info.regs =
        -        (struct memac_mdio_controller *)CONFIG_SYS_FM1_DTSEC_MDIO_ADDR;
        +        (struct memac_mdio_controller *)CONFIG_SYS_FM1_TGEC_MDIO_ADDR;  //mdio2:0xfd000
        dtsec_mdio_info.name = DEFAULT_FM_MDIO_NAME;
	    /* Register the MDIO1 bus */
        fm_memac_mdio_init(bis, &dtsec_mdio_info);

        tgec_mdio_info.regs =
        -       (struct memac_mdio_controller *)CONFIG_SYS_FM1_TGEC_MDIO_ADDR;
        +       (struct memac_mdio_controller *)CONFIG_SYS_FM1_DTSEC_MDIO_ADDR; //mdio1:0xfc000
        tgec_mdio_info.name = DEFAULT_FM_TGEC_MDIO_NAME;
        /* Register the MDIO2 bus */
        fm_memac_mdio_init(bis, &tgec_mdio_info);

        /* Set the four on-board QSGMII PHY address */
        fm_info_set_phy_address(FM1_DTSEC1, QSGMII_PHY1_ADDR);		//phy 地址设置
        fm_info_set_phy_address(FM1_DTSEC5, QSGMII_PHY3_ADDR);
        fm_info_set_phy_address(FM1_DTSEC6, QSGMII_PHY4_ADDR);
        fm_info_set_phy_address(FM1_DTSEC10, QSGMII_PHY2_ADDR);

        dev = miiphy_get_dev_by_name(DEFAULT_FM_MDIO_NAME);			//将mdio总线与mac 设备绑定
        fm_info_set_mdio(FM1_DTSEC1, dev);
        fm_info_set_mdio(FM1_DTSEC5, dev);
        fm_info_set_mdio(FM1_DTSEC6, dev);
        fm_info_set_mdio(FM1_DTSEC10, dev);

        switch (srds_s1) {
        case 0x1040:
                break;
        default:
                printf("Invalid SerDes protocol 0x%x for LS1046ARDB\n",
                       srds_s1);
                break;
        }
        
        cpu_eth_init(bis);
#endif

        return pci_eth_init(bis);
}

下面的枚举定义了mac 的使用类型FM1_DTSEC1-FM1_DTSEC6、FM1_DTSEC9-FM1_DTSEC10 分别代表mac1-mac6 和mac9-10的1G模式,mac9 和mac10 是支持10G的分别用FM1_10GEC1 和FM1_10GEC2 表示。
CONFIG_SYS_FM1_TGEC_MDIO_ADDR、CONFIG_SYS_FM1_DTSEC_MDIO_ADDR 分别是两个mdio总线寄存器地址。

//include/fm_eth.h
enum fm_port {
        FM1_DTSEC1,
        FM1_DTSEC2,
        FM1_DTSEC3,
        FM1_DTSEC4,
        FM1_DTSEC5,
        FM1_DTSEC6,
        FM1_DTSEC9,
        FM1_DTSEC10,
        FM1_10GEC1,
        FM1_10GEC2,
        FM1_10GEC3,
        FM1_10GEC4,
        FM2_DTSEC1,
        FM2_DTSEC2,
        FM2_DTSEC3,
        FM2_DTSEC4,
        FM2_DTSEC5,
        FM2_DTSEC6,
        FM2_DTSEC9,
        FM2_DTSEC10,
        FM2_10GEC1,
        FM2_10GEC2,
        NUM_FM_PORTS,
}

enum fm_eth_type {
        FM_ETH_1G_E,
        FM_ETH_10G_E,
};

/* Historically, on FMan v3 platforms, the first MDIO bus has been used for
 * Clause 22 PHYs and the second MDIO bus for 10G Clause 45 PHYs (thus the
 * TGEC name).
 *
 * On LS1046A-FRWY, the QSGMII PHY is connected to the second MDIO bus,
 * and no TGEC ports are present on-board.
 */
#ifdef CONFIG_SYS_FMAN_V3
#ifdef CONFIG_TARGET_LS1046AFRWY
#define CONFIG_SYS_FM1_DTSEC_MDIO_ADDR  (CONFIG_SYS_FSL_FM1_ADDR + 0xfd000)
#else
#define CONFIG_SYS_FM1_DTSEC_MDIO_ADDR  (CONFIG_SYS_FSL_FM1_ADDR + 0xfc000)
#endif
#define CONFIG_SYS_FM1_TGEC_MDIO_ADDR   (CONFIG_SYS_FSL_FM1_ADDR + 0xfd000)
#if (CONFIG_SYS_NUM_FMAN == 2)
#define CONFIG_SYS_FM2_DTSEC_MDIO_ADDR  (CONFIG_SYS_FSL_FM2_ADDR + 0xfc000)
#define CONFIG_SYS_FM2_TGEC_MDIO_ADDR   (CONFIG_SYS_FSL_FM2_ADDR + 0xfd000)
#endif
#else
#define CONFIG_SYS_FM1_DTSEC1_MDIO_ADDR (CONFIG_SYS_FSL_FM1_ADDR + 0xe1120)
#define CONFIG_SYS_FM1_TGEC_MDIO_ADDR   (CONFIG_SYS_FSL_FM1_ADDR + 0xf1000)
#endif

#define DEFAULT_FM_MDIO_NAME "FSL_MDIO0"
#define DEFAULT_FM_TGEC_MDIO_NAME "FM_TGEC_MDIO"

phy地址宏在板级头文件定义,另外一些其它的配置也在此文件定义,比如phy驱动 :include/configs/ls1046ardb.h

/* phy driver */
#ifdef CONFIG_NET
#define CONFIG_PHY_REALTEK
#define CONFIG_PHY_VITESSE
#endif

#define CONFIG_SYS_DPAA_FMAN
#ifdef CONFIG_SYS_DPAA_FMAN
#define QSGMII_PHY1_ADDR                0x1f
#define QSGMII_PHY2_ADDR                0x1e
#define QSGMII_PHY3_ADDR                0x1d
#define QSGMII_PHY4_ADDR                0x1c

LS1046 RGMII 网口调试

ls1046 的两个rgmii 接口分别是EC1 和EC2,需要注意的是他们俩是共用一个ECGTX 时钟,可以选择其中之一作为时钟源。
uboot 修改的位置和前面的USB 一样,在board/freescale/ls1046ardb/ls1046ardb.c。
在这里插入图片描述

uboot 网口mac地址

uboot 下设置mac 地址,比如我有8个mac设置如下:

setenv ethaddr 00:01:02:03:04:00
setenv eth1addr 00:01:02:03:04:01
setenv eth2addr 00:01:02:03:04:02
setenv eth3addr 00:01:02:03:04:03
setenv eth4addr 00:01:02:03:04:04
setenv eth5addr 00:01:02:03:04:05
setenv eth6addr 00:01:02:03:04:06
setenv eth7addr 00:01:02:03:04:07

另外在uboot 下验证网口也很简单,只需要设置以下环境变量,然后ping 就行:
uboot 默认只工作一个eth设备,可以用变量ethprime 来设置哪个网口工作。

setenv ethprime FM1@DTSEC3
setenv ipaddr 192.168.28.100
setenv gatewayip 192.168.28.254
saveenv
reset

=> ping 192.168.28.13
Using FM1@DTSEC4 device
host 192.168.28.13 is alive

只要答应目标主机alive 则说明ping通了。

各个厂家uboot 的mac 驱动代码都在u-boot/drivers/net 路径中,ls1046 的代码在drivers/net/fm/eth.c
eth.c 调用eth_register(dev); 注册一个mac 设备。

uboot phy调试

在uboot 下可以使用mii 命令读取phy 寄存器
mii info 没有输出时说明mdio 读取phy reg是有问题的。
在这里插入图片描述

uboot 板级头文件

板级头文件include/configs/ls1046ardb.h 和板级文件board/freescale/ls1046ardb/ls1046ardb.c 在uboot中非常重要。
许多配置和定义放在 头文件中,许多关于板子的初始化放在板级文件中。

ls1046ardb.h 里面有很多宏定义,这些宏定义基本用于配置 uboot,也有一些ls1046 的配置项目。如果我们自己要想使能或者禁止 uboot 的某些功能,那就在ls1046ardb.h 里面做修改即可。
ls1046ardb.h 文件中基本都是“CONFIG_”开头的宏定义,这也说明 ls1046ardb.h 文件的主要功能就是配置或者裁剪 uboot。如果需要某个功能的话就在里面添加这个功能对应的 CONFIG_XXX 宏即可,如果不需要某个功能的话就删除掉对应的宏即可。

ls1046ardb.h 添加了头文件 ls1046a_common.h,如果在 ls1046ardb.h 中没有发现有配置某个功能或命令,但是实际却存在的话,可以到 ls1046a_common.h 文件里面去找一下。

uboot 环境变量设置:
ls1046 的环境变量也在ls1046ardb.h 或ls1046a_common.h 中设置。需要注意的环境变量如下:
CONFIG_BOOTCOMMAND 用来设置bootcmd ;
CONFIG_SYS_LOAD_ADDR 内核加载到内存中的地址(这个和sdk uboot 设置的内核加载地址不一样,默认情况参照sdk uboot);

ls1046ardb.h 内容解析

#ifndef __LS1046ARDB_H__
#define __LS1046ARDB_H__

#include "ls1046a_common.h"

#define CONFIG_SYS_CLK_FREQ             100000000				//sys 参考时钟设置100M
#define CONFIG_DDR_CLK_FREQ             100000000				//DDR 参考时钟设置100M

#define CONFIG_LAYERSCAPE_NS_ACCESS

#define CONFIG_DIMM_SLOTS_PER_CTLR      1
/* Physical Memory Map */
#define CONFIG_CHIP_SELECTS_PER_CTRL    4

#define CONFIG_DDR_SPD
#define SPD_EEPROM_ADDRESS              0x51
#define CONFIG_SYS_SPD_BUS_NUM          0

#define CONFIG_DDR_ECC											//DDR ECC 使能
#define CONFIG_ECC_INIT_VIA_DDRCONTROLLER
#define CONFIG_MEM_INIT_VALUE           0xdeadbeef

#ifdef CONFIG_SD_BOOT				//emmc/sd 启动
#define CONFIG_SYS_FSL_PBL_PBI board/freescale/ls1046ardb/ls1046ardb_pbi.cfg		//pbi 配置文件
#ifdef CONFIG_EMMC_BOOT
#define CONFIG_SYS_FSL_PBL_RCW \
        board/freescale/ls1046ardb/ls1046ardb_rcw_emmc.cfg							//emmc rcw 配置文件:转化成16进制的512字节数据
#else
#define CONFIG_SYS_FSL_PBL_RCW board/freescale/ls1046ardb/ls1046ardb_rcw_sd.cfg		//sd rcw
#endif
#elif defined(CONFIG_QSPI_BOOT)		//qspi 启动
#define CONFIG_SYS_FSL_PBL_RCW \
        board/freescale/ls1046ardb/ls1046ardb_rcw_qspi.cfg
#define CONFIG_SYS_FSL_PBL_PBI \
        board/freescale/ls1046ardb/ls1046ardb_qspi_pbi.cfg
#define CONFIG_SYS_UBOOT_BASE           0x40100000
#define CONFIG_SYS_SPL_ARGS_ADDR        0x90000000
#endif


/* PMIC:i2c 接口的电源管理芯片 */
#define CONFIG_POWER
#ifdef CONFIG_POWER
#define CONFIG_POWER_I2C
#endif

/*
 * Environment
 */
#define CONFIG_SYS_MMC_ENV_DEV          0
#define CONFIG_SYS_FSL_QSPI_BASE        0x40000000

#define AQR105_IRQ_MASK                 0x80000000
/* FMan */
#ifndef SPL_NO_FMAN

/* phy driver :phy驱动配置*/
#ifdef CONFIG_NET
#define CONFIG_PHY_REALTEK
#define CONFIG_PHY_VITESSE
#endif

//phy 地址配置
#define CONFIG_SYS_DPAA_FMAN
#ifdef CONFIG_SYS_DPAA_FMAN
#define QSGMII_PHY1_ADDR                0x1f
#define QSGMII_PHY2_ADDR                0x1e
#define QSGMII_PHY3_ADDR                0x1d
#define QSGMII_PHY4_ADDR                0x1c

/*
#define RGMII_PHY1_ADDR                 0x1
#define RGMII_PHY2_ADDR                 0x2
#define SGMII_PHY1_ADDR                 0x3
#define SGMII_PHY2_ADDR                 0x4
#define FM1_10GEC1_PHY_ADDR             0x0
*/
#define FDT_SEQ_MACADDR_FROM_ENV

#define CONFIG_ETHPRIME                 "FM1@DTSEC3"
#endif

#endif

// bootcmd 定义
#ifndef SPL_NO_MISC
#undef CONFIG_BOOTCOMMAND
#ifdef CONFIG_TFABOOT			
#define QSPI_NOR_BOOTCOMMAND "run distro_bootcmd; run qspi_bootcmd; "   \
                           "env exists secureboot && esbc_halt;;"
#define SD_BOOTCOMMAND "run distro_bootcmd;run sd_bootcmd; "    \
                           "env exists secureboot && esbc_halt;"
#else
#if defined(CONFIG_QSPI_BOOT)
#define CONFIG_BOOTCOMMAND "run distro_bootcmd; run qspi_bootcmd; "     \
                           "env exists secureboot && esbc_halt;;"
#elif defined(CONFIG_SD_BOOT)
#define CONFIG_BOOTCOMMAND "run distro_bootcmd;run sd_bootcmd; "        \
                           "env exists secureboot && esbc_halt;"
#endif
#endif
#endif

#include <asm/fsl_secure_boot.h>

#endif /* __LS1046ARDB_H__ */

include/configs/ls1046a_common.h 文件解析
uboot 中的spl 简单认识
SPL 是uboot启动第一阶段代码,只需要包含启动的关键代码即可。

关于distro_bootcmd 的环境变量:
BOOT_TARGET_DEVICES 启动设备定义。
#include <config_distro_bootcmd.h> distro_bootcmd 变量定义。
LS1046A_BOOT_SRC_AND_HDR 启动脚本定义。
CONFIG_EXTRA_ENV_SETTINGS 其它环境变量定义,这个变量是各个uboot 都通用的,想要修改一些环境变量可以修改这个宏。

#ifndef __LS1046A_COMMON_H
#define __LS1046A_COMMON_H

/* SPL build */
#ifdef CONFIG_SPL_BUILD		//SPL 不能太大,不编译这些代码
#define SPL_NO_QBMAN
#define SPL_NO_FMAN
#define SPL_NO_ENV
#define SPL_NO_MISC
#define SPL_NO_QSPI
#define SPL_NO_USB
#define SPL_NO_SATA
#undef CONFIG_DM_I2C
#endif
#if defined(CONFIG_SPL_BUILD) && \
        (defined(CONFIG_NAND_BOOT) || defined(CONFIG_QSPI_BOOT))
#define SPL_NO_MMC
#endif
#if defined(CONFIG_SPL_BUILD)           && \
        !defined(CONFIG_SPL_FSL_LS_PPA)
#define SPL_NO_IFC
#endif

#define CONFIG_REMAKE_ELF
#define CONFIG_GICV2		//GIC:中断控制器

#include <asm/arch/config.h>
#include <asm/arch/stream_id_lsch2.h>

/* Link Definitions */	//初始化栈指针,跳入c代码前需要初始化栈
#ifdef CONFIG_TFABOOT
#define CONFIG_SYS_INIT_SP_ADDR         CONFIG_SYS_TEXT_BASE
#else
#define CONFIG_SYS_INIT_SP_ADDR         (CONFIG_SYS_FSL_OCRAM_BASE + 0xfff0)
#endif

#define CONFIG_SKIP_LOWLEVEL_INIT

#define CONFIG_ENV_OVERWRITE

#define CONFIG_VERY_BIG_RAM
#define CONFIG_SYS_DDR_SDRAM_BASE       0x80000000		//ddr 基地址
#define CONFIG_SYS_FSL_DDR_SDRAM_BASE_PHY       0
#define CONFIG_SYS_SDRAM_BASE           CONFIG_SYS_DDR_SDRAM_BASE
#define CONFIG_SYS_DDR_BLOCK2_BASE      0x880000000ULL

#define CPU_RELEASE_ADDR               secondary_boot_func

/* Generic Timer Definitions 通用定时器频率*/
#define COUNTER_FREQUENCY               25000000        /* 25MHz */

/* Size of malloc() pool */
#define CONFIG_SYS_MALLOC_LEN           (CONFIG_ENV_SIZE + 1024 * 1024)

/* Serial Port */
#define CONFIG_SYS_NS16550_SERIAL
#define CONFIG_SYS_NS16550_REG_SIZE     1
#define CONFIG_SYS_NS16550_CLK          (get_serial_clock())

#define CONFIG_SYS_BAUDRATE_TABLE       { 9600, 19200, 38400, 57600, 115200 }

/* SD boot SPL */		// emmc/sd卡启动,spl 的配置
#ifdef CONFIG_SD_BOOT
#define CONFIG_SPL_MAX_SIZE             0x1f000         /* 124 KiB */
#define CONFIG_SPL_STACK                0x10020000
#define CONFIG_SPL_PAD_TO               0x21000         /* 132 KiB */
#define CONFIG_SPL_BSS_START_ADDR       0x8f000000
#define CONFIG_SPL_BSS_MAX_SIZE         0x80000
#define CONFIG_SYS_SPL_MALLOC_START     (CONFIG_SPL_BSS_START_ADDR + \
                                        CONFIG_SPL_BSS_MAX_SIZE)
#define CONFIG_SYS_SPL_MALLOC_SIZE      0x100000

#ifdef CONFIG_NXP_ESBC
#define CONFIG_U_BOOT_HDR_SIZE                          (16 << 10)
/*
 * HDR would be appended at end of image and copied to DDR along
 * with U-Boot image. Here u-boot max. size is 512K. So if binary
 * size increases then increase this size in case of secure boot as
 * it uses raw u-boot image instead of fit image.
 */
#define CONFIG_SYS_MONITOR_LEN          (0x100000 + CONFIG_U_BOOT_HDR_SIZE)
#else
#define CONFIG_SYS_MONITOR_LEN          0x100000
#endif /* ifdef CONFIG_NXP_ESBC */
#endif

/* I2C */	//使能i2c 总线
#ifndef CONFIG_DM_I2C
#define CONFIG_SYS_I2C
#define CONFIG_SYS_I2C_MXC_I2C1         /* enable I2C bus 1 */
#define CONFIG_SYS_I2C_MXC_I2C2         /* enable I2C bus 2 */
#define CONFIG_SYS_I2C_MXC_I2C3         /* enable I2C bus 3 */
#define CONFIG_SYS_I2C_MXC_I2C4         /* enable I2C bus 4 */
#else
#define CONFIG_I2C_SET_DEFAULT_BUS_NUM
#define CONFIG_I2C_DEFAULT_BUS_NUMBER 0
#endif

/* PCIe */	//使能pcie
#define CONFIG_PCIE1            /* PCIE controller 1 */
#define CONFIG_PCIE2            /* PCIE controller 2 */
#define CONFIG_PCIE3            /* PCIE controller 3 */

#ifdef CONFIG_PCI
#define CONFIG_PCI_SCAN_SHOW
#endif

/* SATA */
#ifndef SPL_NO_SATA
#define CONFIG_SCSI_AHCI_PLAT

#define CONFIG_SYS_SATA                         AHCI_BASE_ADDR

#define CONFIG_SYS_SCSI_MAX_SCSI_ID             1
#define CONFIG_SYS_SCSI_MAX_LUN                 1
#define CONFIG_SYS_SCSI_MAX_DEVICE              (CONFIG_SYS_SCSI_MAX_SCSI_ID * \
                                                CONFIG_SYS_SCSI_MAX_LUN)
#endif

/* Command line configuration */

/* MMC */
#ifndef SPL_NO_MMC
#ifdef CONFIG_MMC
#define CONFIG_SYS_FSL_MMC_HAS_CAPBLT_VS33
#endif
#endif

/* FMan ucode */
#ifndef SPL_NO_FMAN
#define CONFIG_SYS_DPAA_FMAN
#ifdef CONFIG_SYS_DPAA_FMAN
#define CONFIG_SYS_FM_MURAM_SIZE        0x60000
#endif

#ifdef CONFIG_TFABOOT
#define CONFIG_SYS_FMAN_FW_ADDR         0x900000
#else
#ifdef CONFIG_SD_BOOT
/*
 * sd卡启动时PBL/rcw 应该存放在sd/emmc 0x1000(8块)处,大小是1MB
 * PBL SD boot image should stored at 0x1000(8 blocks), the size of the image is
 * about 1MB (2048 blocks), Env is stored after the image, and the env size is
 * 0x2000 (16 blocks), 8 + 2048 + 16 = 2072, enlarge it to 18432(0x4800).
 */
#define CONFIG_SYS_FMAN_FW_ADDR         (512 * 0x4800)
#elif defined(CONFIG_QSPI_BOOT)
#define CONFIG_SYS_FMAN_FW_ADDR         0x40900000
#elif defined(CONFIG_NAND_BOOT)
#define CONFIG_SYS_FMAN_FW_ADDR         (36 * CONFIG_SYS_NAND_BLOCK_SIZE)
#else
#define CONFIG_SYS_FMAN_FW_ADDR         0x60900000
#endif
#endif
#define CONFIG_SYS_QE_FMAN_FW_LENGTH    0x10000
#define CONFIG_SYS_FDT_PAD              (0x3000 + CONFIG_SYS_QE_FMAN_FW_LENGTH)
#endif

/* Miscellaneous configurable options */
#define CONFIG_SYS_LOAD_ADDR    (CONFIG_SYS_DDR_SDRAM_BASE + 0x10000000)

#define CONFIG_HWCONFIG
#define HWCONFIG_BUFFER_SIZE            128

#ifndef CONFIG_SPL_BUILD
#define BOOT_TARGET_DEVICES(func) \			//启动设备定义
        func(SCSI, scsi, 0) \
        func(MMC, mmc, 0) \
        func(USB, usb, 0) \
        func(DHCP, dhcp, na)
#include <config_distro_bootcmd.h>			//distro_bootcmd.h
#endif

//启动脚本定义
#if defined(CONFIG_TARGET_LS1046AFRWY)
#define LS1046A_BOOT_SRC_AND_HDR\
        "boot_scripts=ls1046afrwy_boot.scr\0"   \
        "boot_script_hdr=hdr_ls1046afrwy_bs.out\0"
#elif defined(CONFIG_TARGET_LS1046AQDS)
#define LS1046A_BOOT_SRC_AND_HDR\
        "boot_scripts=ls1046aqds_boot.scr\0"    \
        "boot_script_hdr=hdr_ls1046aqds_bs.out\0"
#else
#define LS1046A_BOOT_SRC_AND_HDR\
        "boot_scripts=ls1046ardb_boot.scr\0"    \
        "boot_script_hdr=hdr_ls1046ardb_bs.out\0"
#endif
#ifndef SPL_NO_MISC
/* Initial environment variables */
#define CONFIG_EXTRA_ENV_SETTINGS               \
        "hwconfig=fsl_ddr:bank_intlv=auto\0"    \
        "ramdisk_addr=0x800000\0"               \
        "ramdisk_size=0x2000000\0"              \
        "bootm_size=0x10000000\0"               \
        "fdt_addr=0x64f00000\0"                 \
        "kernel_addr=0x61000000\0"              \
        "scriptaddr=0x80000000\0"               \
        "scripthdraddr=0x80080000\0"            \
        "fdtheader_addr_r=0x80100000\0"         \
        "kernelheader_addr_r=0x80200000\0"      \
        "load_addr=0xa0000000\0"            \
        "kernel_addr_r=0x81000000\0"            \
        "fdt_addr_r=0x90000000\0"               \
        "ramdisk_addr_r=0xa0000000\0"           \
        "kernel_start=0x1000000\0"              \
        "kernelheader_start=0x600000\0"         \
        "kernel_load=0xa0000000\0"              \
        "kernel_size=0x2800000\0"               \
        "kernelheader_size=0x40000\0"           \
        "kernel_addr_sd=0x8000\0"               \
        "kernel_size_sd=0x14000\0"              \
        "kernelhdr_addr_sd=0x3000\0"            \
        "kernelhdr_size_sd=0x10\0"              \
        "console=ttyS0,115200\0"                \
         CONFIG_MTDPARTS_DEFAULT "\0"           \
        BOOTENV                                 \
        LS1046A_BOOT_SRC_AND_HDR                \
        "scan_dev_for_boot_part="               \
                "part list ${devtype} ${devnum} devplist; "   \
                "env exists devplist || setenv devplist 1; "  \
                "for distro_bootpart in ${devplist}; do "     \
                  "if fstype ${devtype} "                  \
                        "${devnum}:${distro_bootpart} "      \
                        "bootfstype; then "                  \
                        "run scan_dev_for_boot; "            \
                  "fi; "                                   \
                "done\0"                                   \
        "boot_a_script="                                  \
                "load ${devtype} ${devnum}:${distro_bootpart} "  \
                        "${scriptaddr} ${prefix}${script}; "    \
                "env exists secureboot && load ${devtype} "     \
                        "${devnum}:${distro_bootpart} "         \
                        "${scripthdraddr} ${prefix}${boot_script_hdr}; " \
                        "env exists secureboot "        \
                        "&& esbc_validate ${scripthdraddr};"    \
                "source ${scriptaddr}\0" \
        ......(省略一部分其它启动方式的内容)
        "sd_bootcmd=echo Trying load from SD ..;"       \
                "mmcinfo; mmc read $load_addr "         \
                "$kernel_addr_sd $kernel_size_sd && "   \
                "env exists secureboot && mmc read $kernelheader_addr_r "               \
                "$kernelhdr_addr_sd $kernelhdr_size_sd "                \
                " && esbc_validate ${kernelheader_addr_r};"     \
                "bootm $load_addr#$board\0"

#endif

/* Monitor Command Prompt */
#define CONFIG_SYS_CBSIZE               512     /* Console I/O Buffer Size */

#define CONFIG_SYS_MAXARGS              64      /* max command args */

#define CONFIG_SYS_BOOTM_LEN   (64 << 20)      /* Increase max gunzip size */

#include <asm/arch/soc.h>

#endif /* __LS1046A_COMMON_H */

从文件导入环境变量

例如我想从emmc 的分区1 /uEnv.txt 文件导入环境变量,uEnv.txt内容如下:

bootcmd_mmc=saveenv; fatload mmc 0:1 $kernel_addr_r $kernel_image; fatload mmc 0:1 $fdt_addr_r $fdt_image; booti $kernel_addr_r - $fdt_addr_r
bootargs=console=ttyS0,115200 earlycon=uart8250,mmio,0x21c0500 root=/dev/mmcblk0p2 cma=64M rw rootwait

可以执行以下命令:

#0x80200000 是我在内存中保存环境变量的地址
fatload mmc 0:1 0x80200000 uEnv.txt
#fatload命令执行成功后会自动生成一个filesize 的变量表示文件的大小
=> printenv filesize
filesize=106
#从内存导入环境变量
importbootenv=env import -t $env_loadaddr $filesize

驱动中dma api使用

dma api详解
Linux系统中有三种地址:虚拟地址、物理地址和总线地址。
虚拟地址:由物理地址经过虚拟内存系统(TLB、页表等)映射而来,内核使用,驱动中使用的都是虚拟地址。
物理地址:cpu 使用的地址,物理地址保存在“phys_addr_t”或“resource_size_t”的变量中。对于一个硬件设备上的寄存器等设备资源,内核是按照物理地址来管理的。
总线地址:pci、dma 等设备使用的地址,可以通过host bridge 进行物理地址与总线地址翻译。

  1. dma地址
    #include <linux/dma-mapping.h> 中定义了dma_addr_t这种数据类型表示dma地址。
  2. DMA寻址限制
    不同的有不同的寻址限制,一般都是32位(寻址范围是4GB)。
    根据DMA buffer的特性,DMA操作有两种:一种是streaming,DMA buffer是一次性的,用完就算。这种DMA buffer需要自己考虑cache一致性。另外一种是DMA buffer是cache coherent的,软件实现上比较简单,更重要的是这种DMA buffer往往是静态的、长时间存在的。

可以通过以下函数设置dma 掩码:告诉内核支持的dma寻址范围。

int dma_set_mask_and_coherent(struct device *dev, u64 mask);
int dma_set_mask(struct device *dev, u64 mask);
int dma_set_coherent_mask(struct device *dev, u64 mask);

dma_set_mask 是设置streaming dma 掩码。
dma_set_coherent_mask 是设置coherent 的。
dma_set_mask_and_coherent 两个都设置。

fec_main.c 中关于发送队列和接收队列的dma 掩码设置如下:

ret = dma_set_mask_and_coherent(&fep->pdev->dev, DMA_BIT_MASK(32));
if (ret < 0) {
		dev_warn(&fep->pdev->dev, "No suitable DMA available\n");
		return ret;
}

返回0 时表示成功。

  1. 两种类型的 dma 映射
    ① 一致性DMA映射(Consistent DMA mappings )
    Consistent DMA mapping有下面两种特点:
    (1)持续使用该DMA buffer(不是一次性的),因此Consistent DMA总是在初始化的时候进行map,在shutdown的时候unmap。
    (2)CPU和DMA controller在发起对DMA buffer的并行访问的时候不需要考虑cache的影响,也就是说不需要软件进行cache操作,CPU和DMA controller都可以看到对方对DMA buffer的更新。实际上一致性DMA映射中的那个Consistent实际上可以称为coherent,即cache coherent。(普通内存映射 有cache 和内存的同步问题;为了读写性能更好,cpu会优先使用cache中保存的数据,没有及时更新到内存,导致dma 设备使用的是内存中的旧数据)

一般使用Consistent DMA mapping的场景包括:

(1)网卡驱动和网卡DMA控制器往往是通过一些内存中的描述符(形成环或者链)进行交互,这些保存描述符的memory一般采用Consistent DMA mapping。

(2)SCSI硬件适配器上的DMA可以主存中的一些数据结构(mailbox command)进行交互,这些保存mailbox command的memory一般采用Consistent DMA mapping。

(3)有些外设有能力执行主存上的固件代码(microcode),这些保存microcode的主存一般采用Consistent DMA mapping。

上面的这些例子有同样的特性:CPU对memory的修改可以立刻被device感知到,反之亦然。一致性映射可以保证这一点。

② 流式DMA映射(streaming DMA mapping)

流式DMA映射是一次性的,一般是需要进行DMA传输的时候才进行mapping,一旦DMA传输完成,就立刻ummap(除非你使用dma_sync_*的接口,下面会描述)。并且硬件可以为顺序化访问进行优化。

这里的streaming可以被认为是asynchronous,或者是不属于coherent memory范围的。

一般使用streaming DMA mapping的场景包括:

(1)网卡进行数据传输使用的DMA buffer

(2)文件系统中的各种数据buffer,这些buffer中的数据最终到读写到SCSI设备上去,一般而言,驱动会接受这些buffer,然后进行streaming DMA mapping,之后和SCSI设备上的DMA进行交互。

  1. 如何使用coherent DMA mapping的接口?
    1、分配并映射dma buffer
    为了分配并映射一个较大(page大小或者类似)的coherent DMA memory,你需要调用下面的接口:
   dma_addr_t dma_handle;
   cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, gfp);

git 下载源码错误

fatal: unable to access 'https://github.com/nxp/qoriq-mc-binary.git/': gnutls_handshake() failed: The TLS connection was non-properly terminated.
Makefile:120: recipe for target 'mc_bin' failed

原因:国外的网站经常访问失败。
解决方法:
打开vpn,设置代理。
在这里插入图片描述

设置git 代理:
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy https://127.0.0.1:7890

fman(帧管理器) 注册中断

//lnxwrp_fm.c
_errno = devm_request_irq(p_LnxWrpFmDev->dev, p_LnxWrpFmDev->irq, fm_irq, IRQF_SHARED, "fman", p_LnxWrpFmDev);

网络基础知识
git clone https://source.codeaurora.org/external/qoriq/qoriq-components/openwrt.git

./scripts/feeds update -a
./scripts/feeds update -a 遇到的问题

./make download 出现以下问题

curl: (22) The requested URL returned error: 404 Not Found
Download failed.
No more mirrors to try - giving up.
Checking out files from the git repository...
Cloning into 'firewall-2020-07-25-e9b90dfa'...
fatal: unable to access 'https://git.openwrt.org/project/firewall3.git/': server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none
Makefile:62: recipe for target '/home/nxp/openwrt/dl/firewall-2020-07-25-e9b90dfa.tar.xz' failed
make[2]: *** [/home/nxp/openwrt/dl/firewall-2020-07-25-e9b90dfa.tar.xz] Error 128
make[2]: Leaving directory '/home/nxp/openwrt/package/network/config/firewall'
time: package/network/config/firewall/download#0.28#0.10#5.52
package/Makefile:111: recipe for target 'package/network/config/firewall/download' failed
make[1]: *** [package/network/config/firewall/download] Error 2
make[1]: Leaving directory '/home/nxp/openwrt'
make[1]: Entering directory '/home/nxp/openwrt'
make[2]: Entering directory '/home/nxp/openwrt/target/linux'
make[3]: Entering directory '/home/nxp/openwrt/target/linux/layerscape'
make[3]: Nothing to be done for 'download'.
make[3]: Leaving directory '/home/nxp/openwrt/target/linux/layerscape'
make[2]: Leaving directory '/home/nxp/openwrt/target/linux'

解决方案:

**export GIT_SSL_NO_VERIFY=1
#or
git config --global http.sslverify false

busybox login

开机自动登录并 加密码

Base system  --->
	<*> busybox................................ Core utilities for embedded Linux  --->
		Login/Password Management Utilities  --->

选择以下配置
在这里插入图片描述
查看/etc/inittab 文件

::sysinit:/etc/init.d/rcS S boot
::shutdown:/etc/init.d/rcS K shutdown
::askconsole:/usr/libexec/login.sh

查看/usr/libexec/login.sh

#!/bin/sh

[ "$(uci -q get system.@system[0].ttylogin)" = 1 ] || exec /bin/ash --login

exec /bin/login

uci -q get system.@system[0].ttylogin 得知,将/etc/config/system 中的ttylogin 改为1 即可上电自动执行串口登录
在这里插入图片描述

修改密码 执行password命令,密码保存在/etc/shadow 中将修改后的内容替换源码中的package/base-files/files/etc/shadow 文件。

root@OpenWrt:~# passwd
Changing password for root
New password:
Bad password: too weak
Retype password:
passwd: password for root changed by root

root@OpenWrt:~# cat /etc/shadow
root:$1$ioxA/4mD$uILUQxVn4Rexko.oUWJRM0:18997:0:99999:7:::
daemon:*:0:0:99999:7:::
ftp:*:0:0:99999:7:::
network:*:0:0:99999:7:::
nobody:*:0:0:99999:7:::
dnsmasq:x:0:0:99999:7:::
dnsmasq:x:0:0:99999:7:::

新内核编译报错:

在这里插入图片描述
解决办法,修改script/Makefile,在HOSTLDLIBES_extract-cert这个宏后面指定链接路径

HOSTLDLIBES_extract-cert =-L /usr/local/lib/ -lcrypto

openwrt学习网站
openwrt深入学习文档

系统调试

openvpn 配置

参考1
参考2

参照参考1执行命令生成文件,再按照参考2将文件拷贝至pc 配置文件夹。

openwrt 5g ppp拨号

ppp 配置
有关ppp的全选上

-> Device Drivers
-> [*] Network device support
	-> <*> PPP (point-to-point protocol) support
		-> <*> PPP BSD-Compress compression
		-> <*> PPP Deflate compression
		-> [*] PPP filtering
		-> <*> PPP MPPE compression (encryption)
		-> [*] PPP multilink support
		-> <*> PPP over Ethernet
		-> <*> PPP support for async serial ports
		-> <*> PPP support for sync tty ports

usb 串口配置,否则不会生成/dev/ttyUSB0~3

#使能 USBNET 功能
-> Device Drivers
	-> -*- Network device support
		-> USB Network Adapters
			-> -*- Multi-purpose USB Networking Framework
#使能 USB 串口 GSM、 CDMA 驱动
-> Device Drivers
	-> [*] USB support
		-> <*> USB Serial Converter support
			-> <*> USB driver for GSM and CDMA modems
#使能 USB 的 CDC ACM 模式
-> Device Drivers
	-> [*] USB support
		-> <*> USB Modem (CDC ACM) support

ttyUSB 生成打印

[    4.081041] option 4-1:1.0: GSM modem (1-port) converter detected
[    4.087338] usb 4-1: GSM modem (1-port) converter now attached to ttyUSB0
[    4.094282] option 4-1:1.1: GSM modem (1-port) converter detected
[    4.100500] usb 4-1: GSM modem (1-port) converter now attached to ttyUSB1
[    4.107472] option 4-1:1.2: GSM modem (1-port) converter detected
[    4.113680] usb 4-1: GSM modem (1-port) converter now attached to ttyUSB2
[    4.120626] option 4-1:1.3: GSM modem (1-port) converter detected
[    4.126827] usb 4-1: GSM modem (1-port) converter now attached to ttyUSB3

openwrt 配置文件修改

openwrt菜单配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

openwrt vpn 支持

选择编译配置选项
LUCI
->3. Applications
<> luci-app-ocserv… LuCI Support for OpenConnect VPN
<
> luci-app-openvpn… LuCI Support for OpenVPN
OPENVPN_mbedtls_ENABLE_DEF_AUTH=y,选中后的子选项全部选中
在这里插入图片描述

busybox mdev支持

mdev
内核添加以下选项:
CONFIG_PROC_FS
CONFIG_PROC_SYSCTL
CONFIG_NET
CONFIG_UEVENT_HELPER
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"

在开机脚本中添加:
echo /sbin/mdev > /proc/sys/kernel/hotplug #设置系统的hotplug程序为mdev
mdev –s

注意:开机脚本有的系统是/etc/init.d/rcS ,有的是/etc/rc.local
出现问题:
“cannot create /proc/sys/kernel/hotplug: nonexistent directory” 原因是CONFIG_UEVENT_HELPER选项没使能和 CONFIG_UEVENT_HELPER_PATH。

修改/etc/mdev.conf:
以下是u盘与flash 自动挂载配置。
“USB_DIR=cat /proc/mounts | grep "${MDEV}" | cut -f 2 -d " "” 获取到设备挂载的目录。

# null may already exist; therefore ownership has to be changed with command
#usb and sd

sd[a-z][0-9]      0:0 666        @ mkdir -p /mnt/dev/$MDEV; mount /dev/$MDEV /mnt/dev/$MDEV;
sd[a-z]           0:0 666        $ USB_DIR=`cat /proc/mounts | grep "${MDEV}" | cut -f 2 -d " " `; umount $USB_DIR;
mmcblk[0-9]p[0-9] 0:0 666        @ /etc/hotplug.d/sd/sd_insert
mmcblk[0-9]       0:0 666        $ /etc/hotplug.d/sd/sd_remove

openwrt luci 支持

LuCI --->
1. Collections --->
-*- luci
<*> luci-ssl
5. Translations --->
<*> luci-i18n-chinese
<*> luci-i18n-english
重新编译 Openwrt 并更新固件。 然后通过浏览器访问路由器开发板的 LAN 口 IP 地址

gpio 驱动配置
/sys/class/gpio 不存在
在.dtsi 中找到gpio控制器节点,根据compatible 属性找到gpio控制器驱动。

gpio0: gpio@2300000 {
            compatible = "fsl,qoriq-gpio";
            reg = <0x0 0x2300000 0x0 0x10000>;
            interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
            gpio-controller;
            #gpio-cells = <2>;
            interrupt-controller;
            #interrupt-cells = <2>;
        };

输入命令:grep -nr “fsl,qoriq-gpio”

匹配到二进制文件 drivers/gpio/gpio-mpc8xxx.o
drivers/gpio/gpio-mpc8xxx.c:324: { .compatible = “fsl,qoriq-gpio”, },
drivers/gpio/gpio-mpc8xxx.c:392: if (of_device_is_compatible(np, “fsl,qoriq-gpio”))

vi drivers/gpio/Makefile :找到以下编译选项
obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o

make menuconfig 查找CONFIG_GPIO_MPC8XXX 选项并勾选配置。

lan 配置

/etc/config/network 配置完后,ifconfig 仍让没有br-lan(桥接)
内核加上配置选项 CONFIG_BRIDGE 即可。
在这里插入图片描述
当有了br-lan后,lan能ping通 wan口eth0,和192.168.2.1,同时eth0 能ping通上一层网关192.168.28.254,但是lan下的设备却不能ping通192.168.28.254。
在这里插入图片描述
在这里插入图片描述
此时需要配置内核的netfilterCONFIG_NETFILTER(负责数据包的过滤,转发,接受,拒绝等等)。

[*] Networking support  ---> 
	Networking options  ---> 
		[*] Network packet filtering framework (Netfilter)  ---> 

在这里插入图片描述
Core Netfilter Configuration 选择以下配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
IP set support
在这里插入图片描述
IP: Netfilter Configuration
在这里插入图片描述
IPv6 socket lookup support
在这里插入图片描述
Ethernet Bridge nf_tables support
在这里插入图片描述
Ethernet Bridge tables (ebtables) support
在这里插入图片描述

根据git tag修改Linux内核版本名

make menuconfig
LOCALVERSION=”版本名“,LOCALVERSION_AUTO 将这个配置改为n。
vi scripts/setlocalversion

159 # localversion* files in the build and source directory
160 res="$(collect_files localversion*)"
161 if test ! "$srctree" -ef .; then
162     res="$res$(collect_files "$srctree"/localversion*)"
163 fi
164
165 # CONFIG_LOCALVERSION and LOCALVERSION (if set)
166 res="${res}${CONFIG_LOCALVERSION}${LOCALVERSION}"
167
168 # scm version string if not at a tagged commit
169 if test "$CONFIG_LOCALVERSION_AUTO" = "y"; then
170     # full scm version string
171     res="$res$(scm_version)"
172 else
173     # append a plus sign if the repository is not in a clean
174     # annotated or signed tagged state (as git describe only
175     # looks at signed or annotated tags - git tag -a/-s) and
176     # LOCALVERSION= is not specified
177     if test "${LOCALVERSION+set}" != "set"; then
178         scm=$(scm_version --short)
179         #res="$res${scm:++}"
180         tag=$(git describe --tags `git rev-list --tags --max-count=1`)
181         res="$res${scm:+_}$tag"
182     fi
183 fi
184
185 echo "$res"

res 变量的值就是Linux内核的名字,将一下语句做修改:

#默认的
res="$res${scm:++}"			
#修改为
tag=$(git describe --tags `git rev-list --tags --max-count=1`)	#当前标签名
res="$res${scm:+_}$tag"											#在内核名字追加标签

uboot 版本名根据git tag修改

与内核基本一致。

openwrt wifi

openwrt自带hostapd与wpa_supplicant。分别配置hostapd、wpa_supplicant、wpa_cli 和无线设备。
Kernel modules —>
Wireless Drivers —>
-*- kmod-mac80211… Linux 802.11 Wireless Networking Stack —>

编译时出现错误 ,make clean后重新编译成功。

hostapd 频段设置
hw_mode=g 时为2.4G,hw_mode=a 时为5G。

uboot fatload 源码

遇到的问题:在添加看门狗时发现fatload 加载内核时间过长,导致长时间无法喂狗,在fatload 从emmc 读取数据的代码中添加喂狗。
uboot命令行中一些命令的源码在u-boot/cmd 目录下,例如fatload 源码在cmd/fat.c

do_fat_fsload
	->do_load
		time = get_timer(0);
		ret = _fs_read(filename, addr, pos, bytes, 1, &len_read);
		time = get_timer(time);
	
		->_fs_read
			struct fstype_info *info = fs_get_info(fs_type);
			......
			buf = map_sysmem(addr, len);
			ret = info->read(filename, buf, offset, len, actread);		
			unmap_sysmem(buf);

以下代码在fs/fs.c 中
info->read 为 fat_read_file。

fat_read_file	//fat 读取文件
	->file_fat_read_at
		->get_contents //获取内容
			->get_cluster	//获取簇
				->disk_read //磁盘读取
					->blk_dread //块读取	drivers/block/blk-uclass.c
						blks_read = ops->read(dev, start, blkcnt, buffer);

在uboot源码中grep 搜索发现ops->read 为mmc_bread。因为我的flash设备是emmc。
drivers/mmc/mmc-uclass.c

static const struct blk_ops mmc_blk_ops = {
	.read	= mmc_bread,
#if CONFIG_IS_ENABLED(MMC_WRITE)
	.write	= mmc_bwrite,
	.erase	= mmc_berase,
#endif
	.select_hwpart	= mmc_select_hwpart,
};

drivers/mmc/mmc.c

mmc_bread
	->mmc_read_blocks
		->mmc_send_cmd			//该函数为读取磁盘数据

int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
{
	int ret;

	mmmc_trace_before_send(mmc, cmd);
	ret = mmc->cfg->ops->send_cmd(mmc, cmd, data);
	mmmc_trace_after_send(mmc, cmd, ret);

	return ret;
}

搜索发现 mmc->cfg->ops->send_cmd 为fsl_esdhc_send_cmd
drivers/mmc/fsl_esdhc.c

static const struct dm_mmc_ops fsl_esdhc_ops = {
        .get_cd         = fsl_esdhc_get_cd,
        .send_cmd       = fsl_esdhc_send_cmd,
        .set_ios        = fsl_esdhc_set_ios,
#ifdef MMC_SUPPORTS_TUNING
        .execute_tuning = fsl_esdhc_execute_tuning,
#endif
};
fsl_esdhc_send_cmd
	->esdhc_send_cmd_common			//此函数有很多while循环与delay操作,在此函数中添加喂狗即可。

遇到的一些不理解的函数:
get_timer:应该时获取当前时间。

PCIE 电源管理D3 状态配置空间读写出错

在ls1046 上使用QCA639X wifi 模块时出现报错cnss_pci 0000:01:00.0: Refused to change power state, currently in D3,然后wifi 驱动中读写pcie 内存地址,导致内核访问非法地址重启。

static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
{
	dev_info(&dev->dev,"%s change pci power state from D%d to D%d\n",__func__,dev->current_state,state);
    /*
     * Validate current state:
     * Can enter D0 from any state, but if we can only go deeper
     * to sleep if we're already in a low power state
     */
    if (state != PCI_D0 && dev->current_state <= PCI_D3cold
        && dev->current_state > state) {
        pci_err(dev, "invalid power transition (from state %d to %d)\n",
            dev->current_state, state);
        return -EINVAL;
    }

	ret = pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
    dev_info(&dev->dev,"%s read pmcsr 0x%x,ret 0x%x\n",__func__,pmcsr,ret);
    if(ret)
        return -1;
    /*
     * If we're (effectively) in D3, force entire word to 0.
     * This doesn't affect PME_Status, disables PME_En, and
     * sets PowerState to 0.
     */
    switch (dev->current_state) {
    case PCI_D0:
    case PCI_D1:
    case PCI_D2:
        pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
        pmcsr |= state;
        break;
    case PCI_D3hot:
    case PCI_D3cold:
    case PCI_UNKNOWN: /* Boot-up */
        if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
         && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
            need_restore = true;
        /* Fall-through - force to D0 */
    default:
        pmcsr = 0;
        break;
    }

	/* Enter specified state */
    ret = pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
    dev_info(&dev->dev,"%s wirte pmcsr 0x%x,ret 0x%x\n",__func__,pmcsr,ret);
    if(ret)
        return -1;

    /*
     * Mandatory power management transition delays; see PCI PM 1.1
     * 5.6.1 table 18
     */
    if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
        pci_dev_d3_sleep(dev);
    else if (state == PCI_D2 || dev->current_state == PCI_D2)
        udelay(PCI_PM_D2_DELAY);

    ret = pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
    dev_info(&dev->dev,"%s read pmcsr 0x%x,ret 0x%x\n",__func__,pmcsr,ret);
    if(ret)
        return -1;
    dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
    if (dev->current_state != state)
        pci_info_ratelimited(dev, "Refused to change power state, currently in D%d\n",
             dev->current_state);

	return 0;

在代码中添加log 发现pci_read_config_word、pci_write_config_word,在读写pmcsr (电源管理)寄存器时有概率会失败,并且在没有成功切换到D0 的情况下使用lspci -v xxx 命令读取pcie 配置空间也会出错,起初怀疑是硬件信号不稳定导致的。
但是后面发现成功切换到D0 后再用lspci 读就不会出错了,可能是D3 状态有问题。
在这里插入图片描述
在网上搜索有关于D3切换到D0 失败的案例,有的解决方法是在bootargs中添加 pcie_port_pm=off,向内核传参关闭D3状态。
cnss_pci 0000:01:00.0: Refused to change power state, currently in D3

uboot 设置bootargs可以向内核传参数,例如:
bootargs=console=ttyS0,115200 earlycon=uart8250,mmio,0x21c0500 root=/dev/mmcblk0p2 cma=64M rw rootwait pcie_port_pm=off
__setup(“pcie_port_pm=”, pcie_port_pm_setup); 字符串off 会作为pcie_port_pm_setup 函数的参数。

static int __init pcie_port_pm_setup(char *str)
{
    if (!strcmp(str, "off"))
        pci_bridge_d3_disable = true;
    else if (!strcmp(str, "force"))
        pci_bridge_d3_force = true;
    return 1;
}
__setup("pcie_port_pm=", pcie_port_pm_setup);

但是好像设置后没啥用处,依然会读写失败。最终在wifi 驱动编译时加上CONFIG_PCIE_EMULATION=y(不同的wifi 模块配置应该不同),make CONFIG_PCIE_EMULATION=y,可以禁用处理器的D3状态,问题解决。

在这里插入图片描述

openwrt 编译docker

在 配置docker-ce 后编译会出现以下错误:
在这里插入图片描述
解决方法:解决方案
在 feeds/packages/utils/tini/Makefile 中添加:

TARGET_LDFLAGS += $(if $(CONFIG_USE_GLIBC),-static -lc -lgcc_eh,)

在 feeds/packages/utils/docker-ce/Makefile 中添加:

TARGET_LDFLAGS += $(if $(CONFIG_USE_GLIBC),-static -lpthread -lc -lgcc_eh,)

error: ext4_allocate_best_fit_partial: failed to allocate 1057 blocks, out of space?

下载docker镜像

docker run \-d -p 9999:9000 \-p 8000:8000 \--name portainer \--restart always \-v /var/run/docker.sock:/var/run/docker.sock \-v portainer_data:/data \portainer/portainer:linux-arm64

浏览器:{wan ip}:9999,登录docker界面。

openwrt-21.02.0 docker编译问题
解决方案

openwrt 蓝牙使用

内核配置
蓝牙内核配置
openwrt bluez
bluez ble

ksz9897 switch dsa驱动

驱动源码获取:ksz9477 驱动
内核配置:
ksz9897 有两个驱动,一个是switch驱动(drivers/net/ethernet/micrel)、一个是dsa驱动(drivers/net/dsa/microchip)。两个驱动功能不同只能选择其中一个,即使配置两个也只会probe一个驱动。本节选择dsa驱动。
ksz9897 支持三种管理接口spi、i2c和iba,根据你的硬件接口选择合适的配置。

Ksz dsa配置
Make menuconfig
①禁用ksz switch 驱动
KSZ_SWITCH [=n]	
②使用ksz9477 dsa驱动
NET_DSA_MICROCHIP_KSZ9477_I2C [=y]	
NET_DSA_MICROCHIP_KSZ9477 [=y]
NET_DSA_MICROCHIP_KSZ_COMMON [=y]
③使能dsa tag
NET_DSA_TAG_KSZ [=y]
④ 使能switchdev
NET_SWITCHDEV [=y]

设备树配置:

&i2c1 { /* switch */
    status = "okay";
    clock-frequency = <100000>;
    
    i2c_ksz9897: ksz9897@5f {
        compatible = "microchip,ksz9897";
        reg = <0x5f>;
        phy-mode = "sgmii";
        status = "okay";

        pinctrl-names = "default";
        ports {
            #address-cells = <1>;
            #size-cells = <0>;
            port@0 {
                reg = <0>;
                label = "lan1";
            };
            port@1 {
                reg = <1>;
                label = "lan2";
            };
            port@2 {
                reg = <2>;
                label = "lan3";
            };
            port@3 {
                reg = <3>;
                label = "lan4";
            };
            port@4 {
                reg = <4>;
                label = "lan5";
            };
            port@5 {
                reg = <5>;
                label = "lan6";
            };
            port@6 {
                reg = <6>;
                label = "cpu";
                ethernet = <&switch0>;
                fixed-link {
                    speed = <1000>;
                    full-duplex;
                };
            };
        };
    };
switch0: ethernet@e2000 {
        /*phy-handle = <&sgmii_phy3>;*/
        /*fixed-link = <1 1 1000 0 0>;*/
        phy-connection-type = "sgmii";
        net_name = "eth4";
        fixed-link {
            speed = <1000>;
            full-duplex;
        };
    };
switch 结构

直接使用官方的驱动会有一些问题,需要修改一点驱动代码,首先得了解switch 结构才能修改。
普通的mac-phy 结构:

  -----------        
  | CPU     |   RGMII/
  |   ------|   MII   ---------
  |   | MAC |---------|  PHY  |
  |   |     |---------|       |
  |   |-----|   SMI   |_______|
  ----------

mac-switch 结构:

                                            ------------------
                                            |  CPU           |
                                            |   -----------  |
                                            |   | MAC/ephy   |  
                                            |___|__||_____|__|
                                                   ||  ||                                         
                                            SGMII  ||  || Management Interface
                                                   ||  ||
                                                   ||  ||
   ------------------------------------------------||--||------ 
   |   Switch                                      ||  ||    |
   |                                               ||  ||    |
   |                                               ||  ||    |
   |   |-----|   |-----|   |-----|   |-----|    |----------| |
   ____|_____|___|_____|___|_____|___|_____|____|_____|____|_|
         PHY1      PHY2      PHY3      PHY4      PHY/MAC

ksz9897 switch 结构框图:
ksz9897 一共有7个端口,1-5都有mac-phy;6-7只有mac,当其中之一作为cpu port时mac需要设置为phy测,cpu的mac就是mac测,刚好是mac-phy,另一个mac可以外接一个phy,就能作为网口使用。
port1-port5 固定作为次级端口,用作外接其它设备;port6-7可以选择其中一个作为cpu端口,用来连接cpu的mac,负责交换机与cpu之间的数据交换。
switch engine 负责交换cpu_port 与slave_port 之间的数据。
spi/i2c/miim 作为管理接口(配置switch 寄存器)
我使用的是port7(sgmii) 作为cpu端口。
在这里插入图片描述

驱动正常加载的结果

dsa注册完成后会生成lan1~lanN 的slave_netdev(对应次级端口port1-port6),是dsa驱动创建的,在运行dsa驱动前,先会确认主设备是否初始化完成,否则就延迟再加载一遍。
eth4 对应cpu port,是cpu的mac驱动(ls1046:drivers/net/ethernet/freescale/sdk_dpaa/)创建的master_netdev,从设备的很多属性都是和主设备一样的,比如mac地址。
lan1-lan4 在应用层上看就与普通网口一样,可以查看连接信息,ethtool等。
为什么需要dsa驱动:
switch 是用来扩展网口的,通过一个mac就可以扩展出许多网口,但是这些网口都是通过cpu端口与cpu交换,在cpu看来这n个次级网口是同一个网口,不管是哪个口来数据,都是从cpu端口进出,它根本不能区别与哪个次级端交互。
dsa驱动可以在发往switch的数据包中添加tail tag(看switch 芯片是否支持),是的cpu可以识别端口。使用vlan 也可以(不同的vlan不能在同一个lan里)。
在这里插入图片描述
ksz9897 switch尾标格式:
当使能dsa后,交换机接收外部的包转发到cpu端口将会在数据尾部添加1个字节的尾标,以识别数据来自交换机的哪个端口。
同时cpu 通过交换机向外部发数据,必须在数据尾部添加1-2字节尾标表示要通过哪个交换机端口转发。
vlan 介绍
在这里插入图片描述

遇到的问题

问题1:

port@6 {
                reg = <6>;
                label = "cpu";
                ethernet = <&switch0>;
                fixed-link {
                    speed = <1000>;
                    full-duplex;
                };
            };
switch0: ethernet@e2000 {
        /*phy-handle = <&sgmii_phy3>;*/
        /*fixed-link = <1 1 1000 0 0>;*/
        phy-connection-type = "sgmii";
        net_name = "eth4";
        fixed-link {
            speed = <1000>;
            full-duplex;
        };
 };

设备树 cpu端口标明了master_netdev 的mac节点句柄ethernet = <&switch0>;,可以通过这个句柄获取到mac节点。
dsa_port_parse_of 函数of_find_net_device_by_node,根据设备树节点找netdev 找不到,返回EPROBE_DEFER(517)。
of_find_net_device_by_node 函数比较of_node的地址是否相等,来获取到对应的netdev,但是这里of_parse_phandle 获取到的地址与eth4:netdev->dev->parent->of_node 不相等,所以匹配失败,找不到eth4的netdev。
我只能替换of_find_net_device_by_node 用网口名来查找netdev。(不推荐修改内核源码)

//net/dsa/dsa2.c
static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn)
{
	struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0);
	const char *name = of_get_property(dn, "label", NULL);
	bool link = of_property_read_bool(dn, "link");

	dp->dn = dn;

	if (ethernet) {
		struct net_device *master;

		master = of_find_net_device_by_node(ethernet);
		if (!master)
			return -EPROBE_DEFER;

		return dsa_port_parse_cpu(dp, master);
	}

	if (link)
		return dsa_port_parse_dsa(dp);

	return dsa_port_parse_user(dp, name);
} 

问题2:修改sgmii 寄存器,将mac7 设置为phy测
找到netdev后就能正常加载dsa驱动了,生成lan1-lanN。
sgmii 寄存器参考:ksz9897s datasheet:5.5 SGMII Registers (Indirect)
如图:类似于phy寄存器,有control、status、自动协商等等。
在这里插入图片描述
主要观察0x1f8001,当作为cpu端口时,mac7 要设置为phy侧。
设置phy侧时bit4、bit3 需要置1、bit2:1 设置sgmii mode。
在这里插入图片描述
修改文件:drivers/net/dsa/microchip/ksz9477.c
函数ksz9477_config_cpu_port 用于配置cpu端口,可以在这设置sgmii寄存器。

问题3:
现象:lan1 连接pc,给pc能分配ip,并且ping通8.8.8.8,但是dns不对。
lan1 连接linux设备,dhcp 不能分配ip,设置静态ip能ping通。
在pc上用wireshark 抓包发现,pc 不断发出query(询问) baidu.com ip的包,但是pc收到了我的设备发出的response(回答)包。对比dns response包的报文内容也没有出错。连接Linux设备dhcp 服务也是同样的情况。
最后发现要关闭mac 驱动 net_dev->hw_features 的NETIF_F_IP_CSUM,即tcp checksum generation。
原因待研究。
在这里插入图片描述

vsc8514

vsc8514 框图
在这里插入图片描述

SerDes 高速串行接口,serialization and deserialization(序列化和反序列化)

usb转串口驱动解绑

内核上电时有如下打印:

[    3.843746] cdc_acm 5-1.1:1.0: ttyACM0: USB ACM device
[    3.849819] cdc_acm 5-1.1:1.2: ttyACM1: USB ACM device
[    3.855290] cdc_acm 5-1.1:1.4: ttyACM2: USB ACM device
[    3.860773] cdc_acm 5-1.1:1.6: ttyACM3: USB ACM device

将usb 总线设备号写入驱动文件夹中的unbind 文件时,可以解除驱动与设备的绑定。

echo 5-1.1:1.0  > /sys/bus/usb/drivers/cdc_acm/unbind
echo 5-1.1:1.2  > /sys/bus/usb/drivers/cdc_acm/unbind
echo 5-1.1:1.4  > /sys/bus/usb/drivers/cdc_acm/unbind
echo 5-1.1:1.6  > /sys/bus/usb/drivers/cdc_acm/unbind

Linux下查看USB设备命令: cat /sys/kernel/debug/usb/devices

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值