Rockchip rk3566 android11 네트워크 포트 로그 오류: DMA 엔진 초기화 실패

문제 설명: 전원 켜기 초기화 전에 네트워크 포트를 삽입하면 전원 켜기 초기화 네트워크 포트를 정상적으로 사용할 수 있으며 PHY를 찾을 수 있습니다. ifconfig는 eth0을 생성할 수 있는지 확인하고 네트워크 포트는 정상적으로 사용 중이며 핫 스왑이 가능하지만 전원 켜기 초기화 시 네트워크 포트에 삽입되지 않은 경우 DMA 엔진 초기화 실패 오류가 보고되고 DMA 초기화 중에 오류가 발생했습니다.

분석: 일반적으로 이 문제는 GMAC의 작동 시계 문제로 볼 수 있습니다. 먼저 클록 핀에 클록이 있는지, 클록 주파수와 진폭이 정상인지 여부를 측정하고 주로 다음 측면을 확인합니다.

1. IOMUX 오류, 클록 핀 레지스터 값이 올바른지 확인하십시오.

2. 시계 방향과 구성이 하드웨어와 일치하지 않습니다.

3. 클록 트리와 CRU 레지스터를 확인하여 클록 주파수와 클록 활성화 여부를 확인합니다.

DMA: 각 네트워크 카드에는 FIFO 메모리가 있습니다.NIC(Network Interface Controller)의 경우 FIFO 메모리는 시스템 버스를 통해 데이터를 시스템 메모리로 전송하기 전에 LAN에서 수신한 데이터를 버퍼링하는 데 사용됩니다. 시스템 메모리에 대한 안정적인 액세스를 제공하는 고속 이더넷용 직접 메모리 액세스(DMA: Directly Memory Access) 컨트롤러도 있습니다. 드라이버는 물리적 메모리의 연속 부분에 구현된 네트워크 카드용 링 버퍼를 할당합니다. 네트워크 카드에는 일정 크기의 FIFO 메모리가 있으며 DMA 버퍼는 시스템/드라이버에 의해 할당된 연속적인 물리적 메모리입니다.

요약: 네트워크 카드에는 프로세서와 공유되는 메모리에 내장된 원형 버퍼(종종 DMA 링 버퍼라고 함)가 있습니다. 들어오는 각 패킷은 링 버퍼의 다음 사용 가능한 버퍼에 배치되고 인터럽트가 발생합니다. 그런 다음 드라이버는 처리를 위해 네트워크 패킷을 커널의 다른 부분으로 전달하고 새 DMA 버퍼를 링 버퍼에 배치합니다. DMA는 코드의 버퍼 버퍼입니다. DMA는 직접 메모리 액세스로 I/O 장치의 직접 메모리 액세스를 의미하며 CPU 리소스를 거의 사용하지 않습니다. I/O 장치와 메인 메모리가 데이터를 전달하는 동안 CPU는 다른 일을 할 수 있습니다.

코드 분석: 검색을 통해 오류 위치 찾기

파일 경로: kernel/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c


/**
 * stmmac_hw_setup - setup mac in a usable state.
 *  @dev : pointer to the device structure.
 *  Description:
 *  this is the main function to setup the HW in a usable state because the
 *  dma engine is reset, the core registers are configured (e.g. AXI,
 *  Checksum features, timers). The DMA is ready to start receiving and
 *  transmitting.
 *  Return value:
 *  0 on success and an appropriate (-)ve integer as defined in errno.h
 *  file on failure.
 */
static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
{
	struct stmmac_priv *priv = netdev_priv(dev);
	u32 rx_cnt = priv->plat->rx_queues_to_use;
	u32 tx_cnt = priv->plat->tx_queues_to_use;
	u32 chan;
	int ret;

	/* DMA initialization and SW reset */
	ret = stmmac_init_dma_engine(priv); //该函数返回值为-16 DMA 软件初始化失败。
	if (ret < 0) {
		netdev_err(priv->dev, "%s: DMA engine initialization failed\n",
			   __func__);
		return ret;
	}

	/* Copy the MAC addr into the HW  */
	stmmac_set_umac_addr(priv, priv->hw, dev->dev_addr, 0);
    .........
    .........


코드를 계속 따르십시오. . . . . .

 stmmac_init_dma_engine - DMA init.
 * @priv: driver private structure
 * Description:
 * It inits the DMA invoking the specific MAC/GMAC callback.
 * Some DMA parameters can be passed from the platform;
 * in case of these are not passed a default is kept for the MAC or GMAC.
 */
static int stmmac_init_dma_engine(struct stmmac_priv *priv)
{
	u32 rx_channels_count = priv->plat->rx_queues_to_use;
	u32 tx_channels_count = priv->plat->tx_queues_to_use;
	u32 dma_csr_ch = max(rx_channels_count, tx_channels_count);
	struct stmmac_rx_queue *rx_q;
	struct stmmac_tx_queue *tx_q;
	u32 chan = 0;
	int atds = 0;
	int ret = 0;

	if (!priv->plat->dma_cfg || !priv->plat->dma_cfg->pbl) {
		dev_err(priv->device, "Invalid DMA configuration\n");
		return -EINVAL;
	}

	if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
		atds = 1;

	ret = stmmac_reset(priv, priv->ioaddr);//该函数返回-16
	if (ret) {
		dev_err(priv->device, "Failed to reset the dma\n");
		return ret;
	}
    后面初始化DMA 相关的操作都没有执行了
	/* DMA Configuration */
	stmmac_dma_init(priv, priv->ioaddr, priv->plat->dma_cfg, atds);

stmmac_reset 이 함수가 오류를 반환하는 이유는 주로 PHY 초기화 실패 때문입니다.

계속해서 코드를 추적할 수 있는 방법이 없는 것 같으니 먼저 관련 구성을 살펴보겠습니다.

&gmac1 {
	phy-mode = "rgmii"; //使用的接口,一般千兆网使用的是该接口,我的项目是一个千兆网口
	clock_in_out = "input"; //这里是使用的input,意思是使用PHY所产生的时钟供給Gmac.而不是使用的RK的内部时钟。
	snps,reset-gpio = <&gpio0 RK_PC2 GPIO_ACTIVE_LOW>; //复位引脚
	snps,reset-active-low;//低电平有效
	/* Reset time is 20ms, 100ms for rtl8211f */
	snps,reset-delays-us = <0 20000 100000>;//复位时序
	assigned-clocks = <&cru SCLK_GMAC1_RX_TX>, <&cru SCLK_GMAC1>, <&cru CLK_MAC1_OUT>;
	assigned-clock-parents = <&cru SCLK_GMAC1_RGMII_SPEED>, <&gmac1_clkin>;
	assigned-clock-rates = <0>, <125000000>, <25000000>;
	pinctrl-names = "default";
	pinctrl-0 = <&gmac1m1_miim
		     &gmac1m1_tx_bus2
		     &gmac1m1_rx_bus2
		     &gmac1m1_rgmii_clk
		     &gmac1m1_rgmii_bus
		     /*&eth1m1_pins*/
		     &gmac1m1_clkinout>;
	tx_delay = <0x28>;
	rx_delay = <0x11>;
	phy-handle = <&rgmii_phy1>;
	status = "okay";
};
&mdio1 {
	status = "okay";
	rgmii_phy1: phy@4 {
		compatible = "ethernet-phy-ieee802.3-c22";
		reg = <0x4>;
		clocks = <&cru CLK_MAC1_OUT>;
	};
};

구성 해석: RGMII에는 TX_CLK 및 RX_CLK 두 개의 클록이 있습니다. 이 두 클록은 각각 MAC 및 PHY에서 생성됩니다. 이 두 클록의 주파수는 네트워크 속도와 관련이 있습니다. 기가비트 네트워크 속도에서 클록 주파수는 125MHz, 100M은 25MHz, 10M은 2.5MHz입니다. TX_CLK는 RK 내부의 PLL 주파수 분할 또는 주파수 분할 후 외부 클록 입력에 의해 생성될 수 있습니다. 현재 내부 PLL에서 생성되는 클럭보다 더 독립적이고 RK의 내부 주파수 분할 전략에 영향을 받지 않는 외부 입력 클럭을 사용하고 있어 더 안정적입니다. PHY의 경우 클록 소스로 25M 수정 발진기가 필요하므로 이 클록 소스의 주파수를 곱하거나 나누어 RX_CLK를 얻습니다. 또한 대부분의 PHY에는 이러한 출력 핀이 있어 MAC에 대해 위에서 설명한 외부 클록인 MAC으로 클록을 출력할 수 있으며 클록 크기는 125MHz이며 이는 MAC 측에서 TX_CLK의 클록 소스로 사용됩니다. 클럭 방향은 내부 클럭 출력 또는 외부 클럭 입력의 사용을 나타냅니다(이에 대해서는 코드의 주석 참조).

분석: 문제를 좀 더 분석해 보자 전원을 켜고 초기화할 때 네트워크 케이블을 꽂았기 때문에 초기화에 성공할 수 있고 정상적으로 사용할 수 있으며 오류 보고 없이 핫스왑이 가능하다 네트워크 포트의 속도 기가비트 네트워크의 요구 사항도 충족할 수 있으므로 구성 및 드라이버 코드에 문제가 없습니다. 전원을 켜기 위해 네트워크 케이블을 확인하지 않으면 네트워크 포트가 초기화에 실패하고 위의 오류를 보고하며 위에서 DMA가 무엇인지 분석했으므로 여기서는 언급하지 않겠습니다. 시계에 문제가 있어 정보를 확인했습니다. , PHY와 네트워크 포트 사이에 자동 협상 메커니즘이 있을 것입니다. 즉, 네트워크 케이블을 삽입할 때와 네트워크 케이블을 삽입하지 않을 때 하드웨어 상태가 다릅니다. 현재 GMAC에 공급되는 시계는 네트워크 케이블이 삽입된 후에 삽입되는 것으로 의심되고 있기 때문에 이 메커니즘으로 인해 당시 GMAC의 초기화 요구 사항을 충족하는 125M에 도달하게 되었습니다. 네트워크 케이블이 삽입되지 않은 경우, GMAC에 제공되는 PHY 기본 클럭은 25M에 불과하여 성공적으로 초기화되지 않습니다. 100M PHY를 사용할 때 주파수는 50M이고 1000M PHY를 사용할 때 주파수는 125M입니다.

다음으로 계속해서 PHY 데이터 시트를 분석하겠습니다. 여기서는 AR8035를 사용합니다.

하드웨어 엔지니어에게 클럭 속도를 다시 측정하도록 요청했습니다.

네트워크 케이블이 연결되어 있지 않을 때 초기화되는 RX_CLK의 클럭 속도입니다. 2.5M만이 분명히 요구 사항을 충족하지 않습니다.

 초기 전원을 켰을 때 네트워크 케이블을 꽂았을 때 125M 클럭 속도입니다. (이 상태에서 네트워크 포트를 성공적으로 초기화하여 정상적으로 사용할 수 있습니다.)

PHY의 출력 클럭을 제어할 수 있음을 알 수 있으므로 기본적으로 GMAC에 공급되는 25M 클럭이다. GMAC에 필요한 것은 125M 클럭이므로 GMAC 초기화가 실패하고 이전 분석 아이디어도 검증됩니다.

        이유를 찾았으니 이제 연습할 시간입니다. show time ~~~~~~ 지금 우리가 하고 있는 것은 네트워크 케이블을 연결하지 않을 때 GMAC에 대한 PHY 출력을 125M로 만드는 것입니다.

 // To enable AR8035 ouput a 125MHz clk from CLK_25M 
       phy_write(phydev, 0xd, 0x7);
       phy_write(phydev, 0xe, 0x8016);
       phy_write(phydev, 0xd, 0x4007);
       val = phy_read(phydev, 0xe);
       val &= 0xffe3;
       val |= 0x18;
       phy_write(phydev, 0xe, val);

다음에 추가:

다음과 같은 오류가 있습니다.

[ 26.033159] rk_gmac-dwmac fe010000.ethernet: dma 재설정 실패
[ 26.033174] rk_gmac-dwmac fe010000.ethernet eth0: stmmac_hw_setup: DMA 엔진 초기화 실패 [ 26.033197] rk_gmac-dwmac fe010000.ethernet eth0:
stmmac_open: Hw 설정 실패

分析:dma 재설정 실패

현재 DMA HW 재설정에 실패했습니다. ! !

그리고 DMA 초기화 초기화에 성공했습니다. 먼저 코드를 살펴보십시오.

   /*ret = stmmac_reset(priv, priv->ioaddr);
	if (ret) {
		dev_err(priv->device, "Failed to reset the dma\n");
		return ret;
	}

    经过好一阵的查找,发现该函数其实是通过结构体函数指针赋值的,其赋值的函数如下:
const struct stmmac_dma_ops dwmac4_dma_ops = {
	.reset = dwmac4_dma_reset,
	.init = dwmac4_dma_init,
	.init_chan = dwmac4_dma_init_channel,
	.init_rx_chan = dwmac4_dma_init_rx_chan,
	.init_tx_chan = dwmac4_dma_init_tx_chan,
    ...................


int dwmac4_dma_reset(void __iomem *ioaddr)
{
	u32 value = readl(ioaddr + DMA_BUS_MODE);
	int limit;

	/* DMA SW reset */
	value |= DMA_BUS_MODE_SFT_RESET;
	writel(value, ioaddr + DMA_BUS_MODE);
	limit = 10;
	while (limit--) {
		if (!(readl(ioaddr + DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET))
			break;
		mdelay(10);
	}

	if (limit < 0)
		return -EBUSY; //DMA 复位失败,最终返回-16 表示设备忙!!

	return 0;
}

   분석: 이 코드 조각은 실제로 ioaddr과 관련된 값을 쓰고 읽고 있습니다. 저는 현재 관련 레지스터의 매뉴얼을 모르고 관련 레지스터의 비트가 무엇을 나타내는지 모릅니다. 프로젝트는 DMA 재설정에 실패했지만 DMA 초기화는 성공했습니다.

stmmac_dma_init(priv, priv->ioaddr, priv->plat->dma_cfg, atds); //함수가 성공적으로 실행되었습니다.

특정 DMA 재설정 작업의 기능은 무엇입니까?

        여기서 내 접근 방식은 DMA를 재설정하지 않도록 DMA 나머지를 직접 주석 처리하는 것입니다.

/*Removing DMA reset does not affect the use of functions */
	/*ret = stmmac_reset(priv, priv->ioaddr);
	if (ret) {
		dev_err(priv->device, "Failed to reset the dma\n");
		return ret;
	}*/

그런 다음 네트워크 포트, 전원 켜기, 플러그인 초기화 등의 기능을 테스트하면 모두 정상적으로 사용할 수 있으며 핫 스왑 및 관련 네트워크 속도 테스트는 모두 정상입니다. 현재 제가 생각할 수 있는 해결책은 이렇습니다.해당 레지스터의 값의 의미를 확인할 수 없기 때문에 추적이 불가능합니다.더 좋은 방법이 있으시면 공유를 위해 메시지를 남겨주세요.

요약: 문제가 생겼을 때 당황하지 말고, 머리 없는 파리가 되지 말고, 현상을 바탕으로 어디가 잘못되었는지, 어디가 문제가 없는지 먼저 계산한 다음, 어디가 문제인지 추측한 다음 잘 확인하고 분석하십시오. 하드웨어 상태를 통해.

Supongo que te gusta

Origin blog.csdn.net/qq_48709036/article/details/123061409
Recomendado
Clasificación