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功能,除了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
LS 系列使用文档
LS1046A 调试记录
LS1043 DDR 验证
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 配置,如果你是在你自己设计的板子上启动,对于一些关键的配置,需要修改后才能启动。(时钟配置错误无法启动的现象就是没有任何打印)
下图展示了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] 配置。
根据上面的描述,我的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 参数配置
ls系列DDR 参数配置文件:
各种DDR 控制器驱动文件夹:
除了参考文章里添加的宏外,如果你使用的是DDR 颗粒还需要在plat/nxp/drivers/ddr/nxp-ddr/ddr.h 文件中添加以下定义:
#define CONFIG_STATIC_DDR
如果你想查看更多的 DDR 打印信息,可以添加宏定义:
#define DDR_DEBUG
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 一共有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 配置章节)
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 接口分别是EC1 和EC2,需要注意的是他们俩是共用一个ECGTX 时钟,可以选择其中之一作为时钟源。
uboot 修改的位置和前面的USB 一样,在board/freescale/ls1046ardb/ls1046ardb.c。
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 下可以使用mii 命令读取phy 寄存器
mii info 没有输出时说明mdio 读取phy reg是有问题的。
板级头文件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
#endif /* __LS1046ARDB_H__ */
include/configs/ls1046a_common.h 文件解析
uboot 中的spl 简单认识
SPL 是uboot启动第一阶段代码,只需要包含启动的关键代码即可。
关于distro_bootcmd 的环境变量:
BOOT_TARGET_DEVICES 启动设备定义。
#include
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
#include
/* 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 //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
#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详解
Linux系统中有三种地址:虚拟地址、物理地址和总线地址。
虚拟地址:由物理地址经过虚拟内存系统(TLB、页表等)映射而来,内核使用,驱动中使用的都是虚拟地址。
物理地址:cpu 使用的地址,物理地址保存在“phys_addr_t”或“resource_size_t”的变量中。对于一个硬件设备上的寄存器等设备资源,内核是按照物理地址来管理的。
总线地址:pci、dma 等设备使用的地址,可以通过host bridge 进行物理地址与总线地址翻译。
#include
中定义了dma_addr_t这种数据类型表示dma地址。可以通过以下函数设置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 时表示成功。
一般使用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进行交互。
dma_addr_t dma_handle;
cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, gfp);
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
原因:国外的网站经常访问失败。
解决方法:
打开,设置代理。
设置git 代理:
git config --global http.proxy http://127.0.0.1:7890
git config --global https.proxy https://127.0.0.1:7890
//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
开机自动登录并 加密码
Base system --->
<*> busybox................................ Core utilities for embedded Linux --->
Login/Password Management Utilities --->
::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深入学习文档
系统调试
参考1
参考2
参照参考1执行命令生成文件,再按照参考2将文件拷贝至pc 配置文件夹。
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 配置文件修改
选择编译配置选项
LUCI
->3. Applications
<> luci-app-ocserv… LuCI Support for OpenConnect VPN
<> luci-app-open… LuCI Support for OpenVPN
OPENVPN_mbedtls_ENABLE_DEF_AUTH=y,选中后的子选项全部选中
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
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 选项并勾选配置。
/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
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" #在内核名字追加标签
与内核基本一致。
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。
遇到的问题:在添加看门狗时发现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:应该时获取当前时间。
在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状态,问题解决。
在 配置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编译问题
解决方案
驱动源码获取: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 结构才能修改。
普通的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。
原因待研究。
SerDes 高速串行接口,serialization and deserialization(序列化和反序列化)
内核上电时有如下打印:
[ 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