AM335x网络分析+KSZ9031分析(uboot中ping不通问题)

问题背景:

       手头上的任务是将eboot中添加ping外部网的功能,所以就直接从uboot上把ping命令那部分拿过来调试,am335x内部自带mac,ksz9031是phy,目前主板可以设置ksz9031,也就是说mdio是没问题的,然后用的是通用的cpsw网卡驱动,用的cpdma传输,目前死活ping不通,10/100/1000速度下都试过,自动协商也没问题,感觉卡在cpdma那边,没有输出传出去的感觉,想不用dma直接输出数据,不知道如何下手。经过几个星期的折腾终于解决了,问题出在大小端函数和实虚地址上面。

分析:

       AM335x硬件资源上可以支持两个双网口,网口是GMII,RGMII,RMII接口的10/100/1000以太网端口;

       首先来看下它的网络子系统结构图

               AM335x网络分析+KSZ9031分析(uboot中ping不通问题)_第1张图片

                                                                                        图1 AM335x网络子系统结构图

        网络子系统的核心就是这个CPSW_3G的模块。它是个是么结构呢,我的理解他就是的多路选择器,这个模块有3个接口,分别为PORT0,PORT1,PORT2。其中PORT0接口的接口类型是CPPI型,它连接内部DMA,构成网络特有的DMA通道叫做CPDMA。还有两个接口PORT1和PORT2是对外的,他们的接口类型可以选择为GMII,RGMII,RMII这三种中的任意一个,这个有相应的寄存器可以配置的。简单的描述下就是下面这个结构

                                                          AM335x网络分析+KSZ9031分析(uboot中ping不通问题)_第2张图片

                                                                                     图2 AM335x网络子系统简化图

再接着看图1,在uboot中并没有用到中断,采用的是轮询的方式,所以中断int部分暂时不用。系统时钟PRCM的配置,千兆网模式下的时钟是125MHZ,百兆网口需要25MHZ,十兆需要2.5MHZ。Control Moudle中的GMII_SEL,就是图中左下方那块的作用是选择接口模式,也就是将PORT1和PORT2选择为GMII,RGMII,RMII其中一种类型。再往下SCR就是各种配置寄存器了,这个对着TI手册查看即可,再往下有一个特别重要的点,MDIO模块。这里我们要阐述下网卡的概念,网卡是由两部分构成的,一部分是mac,一部分是phy,mac提供mac地址和链路接口,phy用于物理信号的生成接收。一般的像常用的DM9000网卡中这两部分都是集成的。也有些芯片像AM335x,它内部自带mac部分(上面分析的部分),这个时候外部就只需要接phy芯片就行,接口方式就是上面说的GMII,RGMII,RMII。我用的phy芯片是KSZ9031,用的接口方式是RGMII。  phy芯片是需要配置的,而这个配置的专用线就是MDIO,它和IIC比较类似,一根CLK,一根DATA。但它据说比IIC快,具体为什么,没有分析,可能他是专用吧,IIC比较通用。因此它们的连接简图如图3所示:

                                                        AM335x网络分析+KSZ9031分析(uboot中ping不通问题)_第3张图片

                                                                               图3  AM335x网络连接简图

ok,进入正题之前先对网络系统进行初始化:

      初始化过程1,对于phy芯片的初始化Uboot中在board/ti/am335x/board.c或者evm.c中会对phy进行初始化最终通用的phy.c(通用的都是phy,也有些特定的,按情况而定)中的一些初始化配置。

eth_register(dev);
cpsw_mdio_init(name, mdio_base, mdio_div);
bus = miiphy_get_dev_by_name(name);
for_active_slave(slave, priv)
    cpsw_phy_init(dev, slave);

      初始化过程2,对于am335x内部网络模块的初始化。这部分内容在ti的通用网络驱动文件cpsw.c中,Cpsw.c是一个通用的网络驱动,所有的配置,收发的最终实现都在这里面对于AM335X内部网络模块的初始化分为两部分,一部分是mac模块的配置(对照寄存器表一一配置)。另外一部分是对于DMA配置。

初始化过程有个博客讲解的比较好,我直接复制过来了(也添加了一点自己的分析),我有点懒:我会接着他后面详解这个发送流程。

原博客地址是 
https://blog.csdn.net/hahachenchen789/article/details/53339181

转载开始:(一直到下面转载结束)

static int cpsw_init(struct eth_device *dev, bd_t *bis)
{
	struct cpsw_priv	*priv = dev->priv;
	struct cpsw_slave	*slave;
	int i, ret;

	/* soft reset the controller and initialize priv */
	setbit_and_wait_for_clear32(&priv->regs->soft_reset);

	/* initialize and reset the address lookup engine */
	cpsw_ale_enable(priv, 1);
	cpsw_ale_clear(priv, 1);
	cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */

	/* setup host port priority mapping */
	__raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map);
	__raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map);

	/* disable priority elevation and enable statistics on all ports */
	__raw_writel(0, &priv->regs->ptype);

	/* enable statistics collection only on the host port */
	__raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en);

	cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD);

	cpsw_ale_add_ucast(priv, priv->dev->enetaddr, priv->host_port,
			   ALE_SECURE);
	cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << priv->host_port);

	for_each_slave(slave, priv)
		cpsw_slave_init(slave, priv);

	cpsw_update_link(priv);

	/* init descriptor pool */  提交DMA描述符,实际上就是提供描述符链表,先把这个链表构造在这,每有一次数据传输就往里面填值。
	for (i = 0; i < NUM_DESCS; i++) {
		desc_write(&priv->descs[i], hw_next,
			   (i == (NUM_DESCS - 1)) ? 0 : &priv->descs[i+1]);
	}
	priv->desc_free = &priv->descs[0];

	/* initialize channels */
	if (priv->data.version == CPSW_CTRL_VERSION_2) {
		memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));
		priv->rx_chan.hdp       = priv->dma_regs + CPDMA_RXHDP_VER2;
		priv->rx_chan.cp        = priv->dma_regs + CPDMA_RXCP_VER2;
		priv->rx_chan.rxfree    = priv->dma_regs + CPDMA_RXFREE;

		memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));
		priv->tx_chan.hdp       = priv->dma_regs + CPDMA_TXHDP_VER2;
		priv->tx_chan.cp        = priv->dma_regs + CPDMA_TXCP_VER2;
	} else {
		memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));
		priv->rx_chan.hdp       = priv->dma_regs + CPDMA_RXHDP_VER1;
		priv->rx_chan.cp        = priv->dma_regs + CPDMA_RXCP_VER1;
		priv->rx_chan.rxfree    = priv->dma_regs + CPDMA_RXFREE;

		memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));
		priv->tx_chan.hdp       = priv->dma_regs + CPDMA_TXHDP_VER1;
		priv->tx_chan.cp        = priv->dma_regs + CPDMA_TXCP_VER1;
	}

	/* clear dma state */
	setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET);

	if (priv->data.version == CPSW_CTRL_VERSION_2) {
		for (i = 0; i < priv->data.channels; i++) {
			__raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4
					* i);
			__raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4
					* i);
			__raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER2 + 4
					* i);
			__raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER2 + 4
					* i);
			__raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER2 + 4
					* i);
		}
	} else {
		for (i = 0; i < priv->data.channels; i++) {
			__raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER1 + 4
					* i);
			__raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4
					* i);
			__raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER1 + 4
					* i);
			__raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER1 + 4
					* i);
			__raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER1 + 4
					* i);

		}
	}

	__raw_writel(1, priv->dma_regs + CPDMA_TXCONTROL);
	__raw_writel(1, priv->dma_regs + CPDMA_RXCONTROL);

	/* submit rx descs */
	for (i = 0; i < PKTBUFSRX; i++) {
		ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i],
				   PKTSIZE);
		if (ret < 0) {
			printf("error %d submitting rx desc\n", ret);
			break;
		}
	}

	return 0;
}

 

首先是声明几个结构体变量,其中包括cpsw的主:cpsw_privcpsw_slave,然后是设置cpsw的基础寄存器的地址cpsw_base,然后调用calloc函数为这些结构体分配空间。

分配好后对priv结构体中的成员进行初始化,host_port=0表示主机端口号是0,然后成员的寄存器的偏移地址进行初始化。

host_port       = host_port_num;
regs        = regs;
host_port_regs  = regs + host_port_reg_ofs;
dma_regs        = regs + cpdma_reg_ofs;
ale_regs        = regs + ale_reg_ofs;
descs       = (void *)regs + bd_ram_ofs;

对每个salve进行初始化,这里采用for循环的意义在于可能有多个网卡,am335x支持双网卡。

for_each_slave(slave, priv) {
    cpsw_slave_setup(slave, idx, priv);
    idx = idx + 1;
}

以mdio方式对网卡配置进行初始化:主要是调用cpsw_phy_init函数进行初始化

eth_register(dev);
cpsw_mdio_init(name, mdio_base, mdio_div);
bus = miiphy_get_dev_by_name(name);
for_active_slave(slave, priv)
    cpsw_phy_init(dev, slave);

cpsw_phy_init函数定义:

static int cpsw_phy_init(struct eth_device *dev, struct cpsw_slave *slave)
{
    struct cpsw_priv *priv = (struct cpsw_priv *)priv;
    struct phy_device *phydev;
    u32 supported = PHY_GBIT_FEATURES;
    printf("cpsw_phy_init \n");
    printf("phy_addr:%d \n", phy_addr);
    phydev = phy_connect(bus, phy_addr,
            dev, phy_if);

    if (!phydev)
        return -1;

    phydev->supported= supported;
    phydev->advertising = phydev->supported;

    priv->phydev = phydev;
    phy_config(phydev);

    return 1;
}

该函数调用phy_connect函数连接网卡,返回的值如果合理就调用phy_config函数对该网卡进行配置,主要是配置网卡的速率和半双工。

首先分析phy_connect函数:

struct phy_device *phy_connect(struct mii_dev *bus, int addr,
        struct eth_device *dev, phy_interface_t interface)
{
    struct phy_device *phydev;
    phydev = phy_find_by_mask(bus, addr, interface);
    if (phydev)
        phy_connect_dev(phydev, dev);
    else
        printf("Could not get PHY for %s: addr %d\n", bus->name, addr);
    return phydev;
}

该函数首先调用phy_find_by_mask函数查询网卡设备,如果存在则调用phy_connect_dev函数连接,否则就打印出错信息

phy_connect_dev函数实现:

struct phy_device *phy_find_by_mask(struct mii_dev *bus, unsigned phy_mask,
        phy_interface_t interface)
{
    /* Reset the bus */
    if (bus->reset) {
        bus->reset(bus);

        /* Wait 15ms to make sure the PHY has come out of hard reset */
        udelay(15000);
    }

    return get_phy_device_by_mask(bus, phy_mask, interface);
}

该函数主要是调用get_phy_device_by_mask函数进行设备的查找,get_phy_device_by_mask函数的实现至关重要,包含了对于网卡的主要mdio通信

get_phy_device_by_mask函数实现:

static struct phy_device *get_phy_device_by_mask(struct mii_dev *bus,
        unsigned phy_mask, phy_interface_t interface)
{
    int i;
    struct phy_device *phydev;

    phydev = search_for_existing_phy(bus, phy_mask, interface);
    if (phydev)
        return phydev;
    /* Try Standard (ie Clause 22) access */
    /* Otherwise we have to try Clause 45 */
    for (i = 0; i < 5; i++) {
        phydev = create_phy_by_mask(bus, phy_mask,
                i ? i : MDIO_DEVAD_NONE, interface);
        if (IS_ERR(phydev))
            return NULL;
        if (phydev)
            return phydev;
    }
    printf("Phy %d not found\n", ffs(phy_mask) - 1);
    return phy_device_create(bus, ffs(phy_mask) - 1, 0xffffffff, interface);
}

该函数首先调用search_for_existing_phy函数查找当前存在的设备,如果存在则将该设备返回,不存在则调用create_phy_by_mask函数进行创建。 
search_for_existing_phy函数实现:

static struct phy_device *search_for_existing_phy(struct mii_dev *bus,
        unsigned phy_mask, phy_interface_t interface)
{
    /* If we have one, return the existing device, with new interface */
    while (phy_mask) {
        int addr = ffs(phy_mask) - 1;
        if (bus->phymap[addr]) {
            bus->phymap[addr]->interface = interface;
            return bus->phymap[addr];
        }
        phy_mask &= ~(1 << addr);
    }
    return NULL;
}

create_phy_by_mask函数实现:

static struct phy_device *create_phy_by_mask(struct mii_dev *bus,
        unsigned phy_mask, int devad, phy_interface_t interface)
{
    u32 phy_id = 0xffffffff;
    while (phy_mask) {
        int addr = ffs(phy_mask) - 1;
        int r = get_phy_id(bus, addr, devad, &phy_id);
        /* If the PHY ID is mostly f's, we didn't find anything */
        if (r == 0 && (phy_id & 0x1fffffff) != 0x1fffffff)
            return phy_device_create(bus, addr, phy_id, interface);
        phy_mask &= ~(1 << addr);
    }
    return NULL;
}

该函数调用get_phy_id函数让处理器通过mdio总线查看网卡寄存器存储的ID,如果ID都是f,说明没有ID,就返回空,否则返回phy_device_create函数进行创建一个网卡设备。 
get_phy_id函数实现:

int __weak get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id)
{
    int phy_reg;

    /* Grab the bits from PHYIR1, and put them
     * in the upper half */
    phy_reg = bus->read(bus, addr, devad, MII_PHYSID1);

    if (phy_reg < 0)
        return -EIO;

    *phy_id = (phy_reg & 0xffff) << 16;

    /* Grab the bits from PHYIR2, and put them in the lower half */
    phy_reg = bus->read(bus, addr, devad, MII_PHYSID2);

    if (phy_reg < 0)
        return -EIO;

    *phy_id |= (phy_reg & 0xffff);

    return 0;
}

该函数就调用了bus->read总线读函数,来读取网卡寄存器的值,这里是读取寄存器存储的网卡ID,bus->read函数定义为cpsw_mdio_readcpsw_mdio_read实现:

static int cpsw_mdio_read(struct mii_dev *bus, int phy_id,
                int dev_addr, int phy_reg)
{
    int data;
    u32 reg;
    printf("cpsw_mdio_read \n");
    printf("phy_id %d\n",phy_id);
    if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
        return -EINVAL;

    wait_for_user_access();
    reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
           (phy_id << 16));
    __raw_writel(reg, &mdio_regs->user[0].access);
    reg = wait_for_user_access();

    data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1;
    return data;
}

该函数调用wait_for_user_access函数来等待能否读取寄存器信号,当标志位表示能够access时,将reg的值写到&mdio_regs->user[0].access寄存器,reg包含了想要读的寄存器的变量,然后继续调用wait_for_user_access函数等待数据完成,将wait_for_user_access函数的返回值给reg,想要读寄存器的data就等于(reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1; 然后返回data,就完成了mdio对于网卡寄存器的读取。

看看wait_for_user_access函数的实现:

static inline u32 wait_for_user_access(void)
{
    u32 reg = 0;
    int timeout = MDIO_TIMEOUT;

    while (timeout-- &&
    ((reg = __raw_readl(&mdio_regs->user[0].access)) & USERACCESS_GO))
        udelay(10);

    if (timeout == -1) {
        printf("wait_for_user_access Timeout\n");
        return -ETIMEDOUT;
    }
    return reg;
}

可以看出,这里用了一个while循环,等待100个机器周期来读取网卡寄存器的值。

以上就是cpsw的注册和网卡设备的mdio连接。 
接下来分析最重要的cpsw_init函数,包含了ALE、DMA、cpsw_slave的初始化和配置。

cpsw_init函数实现:

static int cpsw_init(struct eth_device *dev, bd_t *bis)
{
    struct cpsw_priv    *priv = dev->priv;
    struct cpsw_slave   *slave;
    int i, ret;
    printf("cpsw_init func\n");
    /* soft reset the controller and initialize priv */
    setbit_and_wait_for_clear32(&priv->regs->soft_reset);

    /* initialize and reset the address lookup engine */
    cpsw_ale_enable(priv, 1);
    cpsw_ale_clear(priv, 1);
    cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */

    /* setup host port priority mapping */
    __raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map);
    __raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map);

    /* disable priority elevation and enable statistics on all ports */
    __raw_writel(0, &priv->regs->ptype);

    /* enable statistics collection only on the host port */
    __raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en);
    __raw_writel(0x7, &priv->regs->stat_port_en);
    printf("&priv->regs->stat_port_en:%x\n", &priv->regs->stat_port_en);
     printf("priv->regs->stat_port_en:%x\n", priv->regs->stat_port_en);

    cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD);

    //cpsw_ale_add_ucast(priv, priv->dev->enetaddr, priv->host_port, ALE_SECURE);
    //cpsw_ale_add_mcast(priv, net_bcast_ethaddr, 1 << priv->host_port);

    for_active_slave(slave, priv)
        cpsw_slave_init(slave, priv);

    cpsw_update_link(priv);

    /* init descriptor pool */
    for (i = 0; i < NUM_DESCS; i++) {
        desc_write(&priv->descs[i], hw_next,
               (i == (NUM_DESCS - 1)) ? 0 : &priv->descs[i+1]);
    }
    priv->desc_free = &priv->descs[0];
    printf("&priv->descs[0]:%x\n", &priv->descs[0]);
    printf("priv->dma_regs + CPDMA_RXHDP_VER2:%x\n",priv->dma_regs + CPDMA_RXHDP_VER2);

    /* initialize channels */
    if (priv->data.version == CPSW_CTRL_VERSION_2) {
        memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));
        priv->rx_chan.hdp       = priv->dma_regs + CPDMA_RXHDP_VER2;
        priv->rx_chan.cp        = priv->dma_regs + CPDMA_RXCP_VER2;
        priv->rx_chan.rxfree    = priv->dma_regs + CPDMA_RXFREE;

        memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));
        priv->tx_chan.hdp       = priv->dma_regs + CPDMA_TXHDP_VER2;
        priv->tx_chan.cp        = priv->dma_regs + CPDMA_TXCP_VER2;
    } else {
        memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));
        priv->rx_chan.hdp       = priv->dma_regs + CPDMA_RXHDP_VER1;
        priv->rx_chan.cp        = priv->dma_regs + CPDMA_RXCP_VER1;
        priv->rx_chan.rxfree    = priv->dma_regs + CPDMA_RXFREE;

        memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));
        priv->tx_chan.hdp       = priv->dma_regs + CPDMA_TXHDP_VER1;
        priv->tx_chan.cp        = priv->dma_regs + CPDMA_TXCP_VER1;
    }

    /* clear dma state */
    setbit_and_wait_for_clear32(priv->dma_regs + CPDMA_SOFTRESET);

    if (priv->data.version == CPSW_CTRL_VERSION_2) {
        for (i = 0; i < priv->data.channels; i++) {
            __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4
                    * i);
            __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4
                    * i);
            __raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER2 + 4
                    * i);
            __raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER2 + 4
                    * i);
            __raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER2 + 4
                    * i);
        }
    } else {
        for (i = 0; i < priv->data.channels; i++) {
            __raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER1 + 4
                    * i);
            __raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4
                    * i);
            __raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER1 + 4
                    * i);
            __raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER1 + 4
                    * i);
            __raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER1 + 4
                    * i);

        }
    }

    __raw_writel(1, priv->dma_regs + CPDMA_TXCONTROL);
    __raw_writel(1, priv->dma_regs + CPDMA_RXCONTROL);

    /* submit rx descs */
    for (i = 0; i < PKTBUFSRX; i++) {
        ret = cpdma_submit(priv, &priv->rx_chan, net_rx_packets[i],
                   PKTSIZE);
        if (ret < 0) {
            printf("error %d submitting rx desc\n", ret);
            break;
        }
    }

    return 0;
}

首先是ALE的初始化: 
ALE:address lookup engine 地址查询引擎,是TI创造的一种对于双网卡选择的方式:

/* initialize and reset the address lookup engine */
    cpsw_ale_enable(priv, 1);
    cpsw_ale_clear(priv, 1);
    cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */

这三个函数的实现都在cpsw.c文件中,都是向相应的ale寄存器中写值,目的是为了使能ale引擎,并开启vlan(虚拟局域网)

贴出代码,但不具体解释:

#define cpsw_ale_enable(priv, val)  cpsw_ale_control(priv, 31, val)
#define cpsw_ale_clear(priv, val)   cpsw_ale_control(priv, 30, val)
#define cpsw_ale_vlan_aware(priv, val)  cpsw_ale_control(priv,  2, val)

static inline void cpsw_ale_control(struct cpsw_priv *priv, int bit, int val)
{
    u32 tmp, mask = BIT(bit);

    tmp  = __raw_readl(priv->ale_regs + ALE_CONTROL);
    tmp &= ~mask;
    tmp |= val ? mask : 0;
    __raw_writel(tmp, priv->ale_regs + ALE_CONTROL);
}

接下来设置端口的初始mapping,也就是设置内存映射,将硬件DMA通道的发送和接收寄存器的硬件地址映射到内存空间,这样就可通过访问和修改内存地址内容来修改相应硬件配置:

/* setup host port priority mapping */
    __raw_writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map);
    __raw_writel(0, &priv->host_port_regs->cpdma_rx_chan_map);

    /* disable priority elevation and enable statistics on all ports */
    __raw_writel(0, &priv->regs->ptype);

    /* enable statistics collection only on the host port */
    __raw_writel(BIT(priv->host_port), &priv->regs->stat_port_en);
    __raw_writel(0x7, &priv->regs->stat_port_en);

接着,初始化cpsw的slave,也就是网卡部分,具体实现在cpsw_slave_init函数,待会再分析。

for_active_slave(slave, priv)
        cpsw_slave_init(slave, priv);

接着是cpsw_update_link函数,该函数是刷新与网卡的连接,确保能够通信。 
该函数的实现主要是调用cpsw_slave_update_link函数对slave进行重新初始化连接:

函数实现:

static void cpsw_slave_update_link(struct cpsw_slave *slave,
                   struct cpsw_priv *priv, int *link)
{
    struct phy_device *phy;
    u32 mac_control = 0;

    phy = priv->phydev;

    if (!phy)
        return;

    phy_startup(phy);
    *link = phy->link;

    if (*link) { /* link up */
        mac_control = priv->data.mac_control;
        if (phy->speed == 1000)
            mac_control |= GIGABITEN;
        if (phy->duplex == DUPLEX_FULL)
            mac_control |= FULLDUPLEXEN;
        if (phy->speed == 100)
            mac_control |= MIIEN;
    }

    if (mac_control == slave->mac_control)
        return;

    if (mac_control) {
        printf("link up on port %d, speed %d, %s duplex\n",
                slave->slave_num, phy->speed,
                (phy->duplex == DUPLEX_FULL) ? "full" : "half");
    } else {
        printf("link down on port %d\n", slave->slave_num);
    }

    __raw_writel(mac_control, &slave->sliver->mac_control);
    slave->mac_control = mac_control;
}

该函数调用phy_startup(phy)进行设备的开启和连接,然后获得数据进行判断,当link为真时,进入if,判断网卡的工作速率是在10M还是100M,工作模式是双工还是单工。并且通过printf打印信息。

 

对于phy_startup函数主要是调用phy.c文件下的genphy_update_link函数和genphy_parse_link函数。

genphy_update_link实现如下:

int genphy_update_link(struct phy_device *phydev)
{
    unsigned int mii_reg;

    /*
     * Wait if the link is up, and autonegotiation is in progress
     * (ie - we're capable and it's not done)
     */
    mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);

    /*
     * If we already saw the link up, and it hasn't gone down, then
     * we don't need to wait for autoneg again
     */
    if (phydev->link && mii_reg & BMSR_LSTATUS)
        return 0;

    if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {
        int i = 0;

        printf("%s Waiting for PHY auto negotiation to complete",
            phydev->dev->name);
        while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
            /*
             * Timeout reached ?
             */
            if (i > PHY_ANEG_TIMEOUT) {
                printf(" TIMEOUT !\n");
                phydev->link = 0;
                return 0;
            }

            if (ctrlc()) {
                puts("user interrupt!\n");
                phydev->link = 0;
                return -EINTR;
            }

            if ((i++ % 500) == 0)
                printf(".");

            udelay(1000);   /* 1 ms */
            mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
        }
        printf(" done\n");
        phydev->link = 1;
    } else {
        /* Read the link a second time to clear the latched state */
        mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);

        if (mii_reg & BMSR_LSTATUS)
            phydev->link = 1;
        else
            phydev->link = 0;
    }

    return 0;
}

该函数首先调用phy_read函数来获取BMSR寄存器的值

什么是BMSR,这是网卡的状态寄存器,BMCR是网卡的控制寄存器,一般而言,BMSR供我们读取数据进行判断网卡的状态,而BMCR一般是供我们写入数据进行控制。

下图是该网卡的寄存器表:

AM335x网络分析+KSZ9031分析(uboot中ping不通问题)_第4张图片

如果想要查看各寄存器的定义和每一位的定义,可以到SMSC官网下载文档。

回到函数,当读取到网卡的状态寄存器的值后,开始进行一系列判断

 

if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE))这个判断条件是判断网卡是否完成自适应配置,如果完成,打印相应信息。不然的话,第二次读取BMSR寄存器,重新判断一次。

然后是genphy_config函数,该函数是对网卡的信息进行一个读取,比如是否支持千兆网卡,是否支持10M/100M 单工/双工。

函数实现:

int genphy_config(struct phy_device *phydev)
{
    int val;
    u32 features;

    /* For now, I'll claim that the generic driver supports
     * all possible port types */
    features = (SUPPORTED_TP | SUPPORTED_MII
            | SUPPORTED_AUI | SUPPORTED_FIBRE |
            SUPPORTED_BNC);

    /* Do we support autonegotiation? */
    val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);

    if (val < 0)
        return val;

    if (val & BMSR_ANEGCAPABLE)
        features |= SUPPORTED_Autoneg;

    if (val & BMSR_100FULL)
        features |= SUPPORTED_100baseT_Full;
    if (val & BMSR_100HALF)
        features |= SUPPORTED_100baseT_Half;
    if (val & BMSR_10FULL)
        features |= SUPPORTED_10baseT_Full;
    if (val & BMSR_10HALF)
        features |= SUPPORTED_10baseT_Half;

    if (val & BMSR_ESTATEN) {
        val = phy_read(phydev, MDIO_DEVAD_NONE, MII_ESTATUS);

        if (val < 0)
            return val;

        if (val & ESTATUS_1000_TFULL)
            features |= SUPPORTED_1000baseT_Full;
        if (val & ESTATUS_1000_THALF)
            features |= SUPPORTED_1000baseT_Half;
        if (val & ESTATUS_1000_XFULL)
            features |= SUPPORTED_1000baseX_Full;
        if (val & ESTATUS_1000_XHALF)
            features |= SUPPORTED_1000baseX_Half;
    }

    phydev->supported = features;
    phydev->advertising = features;

    genphy_config_aneg(phydev);

    return 0;
}

该函数和genphy_update_link的实现风格很像,不再详细说明 
再回到cpsw_slave_update_link函数,这样就完成了对于网卡的重新连接。 
回到cpsw_init函数,接着初始化DMA通道和DMA描述符 
首先初始化描述符

/* init descriptor pool */
    for (i = 0; i < NUM_DESCS; i++) {
        desc_write(&priv->descs[i], hw_next,
               (i == (NUM_DESCS - 1)) ? 0 : &priv->descs[i+1]);
    }
    priv->desc_free = &priv->descs[0];

然后初始化DMA通道,am335有8个channel

memset(&priv->rx_chan, 0, sizeof(struct cpdma_chan));
priv->rx_chan.hdp       = priv->dma_regs + CPDMA_RXHDP_VER2;
priv->rx_chan.cp        = priv->dma_regs + CPDMA_RXCP_VER2;
priv->rx_chan.rxfree    = priv->dma_regs + CPDMA_RXFREE;

memset(&priv->tx_chan, 0, sizeof(struct cpdma_chan));
priv->tx_chan.hdp       = priv->dma_regs + CPDMA_TXHDP_VER2;
priv->tx_chan.cp        = priv->dma_regs + CPDMA_TXCP_VER2;

分配好channel的内存地址后,初始化这些通道,方法也很简单,写0即可:

__raw_writel(0, priv->dma_regs + CPDMA_RXHDP_VER2 + 4
        * i);
__raw_writel(0, priv->dma_regs + CPDMA_RXFREE + 4
        * i);
__raw_writel(0, priv->dma_regs + CPDMA_RXCP_VER2 + 4
        * i);
__raw_writel(0, priv->dma_regs + CPDMA_TXHDP_VER2 + 4
        * i);
__raw_writel(0, priv->dma_regs + CPDMA_TXCP_VER2 + 4
        * i);

以上就完成了对cpsw设备的初始化,网卡的配置也基本完成

3、实现网卡的发送 
网卡的发送函数主要是调用cpsw_send函数,比如当输入ping命令时,经过一系列的装包,最后调用cpsw_send函数进行发送,在发送ICMP包之前,会先调用arp发送arp地址解析协议,然后根据收到的arp的包得知主机的mac地址。然后再发送icmp包,因此在这里首先要看如何实现arp包的发送。

首先了解arp协议的格式:

                     AM335x网络分析+KSZ9031分析(uboot中ping不通问题)_第5张图片

                                                                                           图4

然后分析cpsw_send函数:

static int cpsw_send(struct eth_device *dev, void *packet, int length)
{
    struct cpsw_priv    *priv = dev->priv;
    void *buffer;
    int len;
    int timeout = CPDMA_TIMEOUT;

    flush_dcache_range((unsigned long)packet,
               (unsigned long)packet + length);

    /* first reap completed packets */
    while (timeout-- &&
        (cpdma_process(priv, &priv->tx_chan, &buffer, &len) &= 0))
        ;

    if (timeout == -1) {
        printf("cpdma_process timeout\n");
        return -ETIMEDOUT;
    }

    return cpdma_submit(priv, &priv->tx_chan, packet, length);
}

该函数调用flush_dcache_range函数对数据缓存进行刷新,刷新的地址就是packet的地址,因为要保证缓存和内存的一致性,也就是一致性DMA。然后调用cpdma_process和cpdma_submit函数将数据传给DMA描述符,再传送给DMA通道,DMA通过MII发送给网卡,网卡再将数据发送出去。 
转载结束。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。地址: 
https://blog.csdn.net/hahachenchen789/article/details/53339181

 UBOOT中对于网络的收发机制是基于描述符DMA的,它的每一次传输或者接收都有相应的一个描述符,这个描述符里包含的信息如下图5是发送描述符,图6是接收描述符。一个描述符包含了一次传输的所有信息,看图中可以看出报货,下一个描述符的地址,数据buffer的地址,数据的长度,SOP,EOP,Owner,EOQ等等传输状态信息,这些状态信息,我们人为 在传输前的设置初值,然后传输过程中,这些值会由芯片改变,我们去读取这些值,可以判断有没有传输完成,有没有收完。

Start of Packet(SOP) Flag

若置位,表示描述符指针指向一个新报文的缓冲区。如是单个帧报文,sop和结束帧eop标志置位。否则,描述符指针指向上一个报文缓冲区,此报文EOP标志置位。此位通过软件应用置位,不允许EMAC修改。

End of Packet(EOP)Flag

若置位,表示描述符指针指向当下报文的最后。若是一个单个的帧报文,sop和eop同时置位。否则,描述符指针指向设置eop标志的报文的包缓冲区最后。软件应该置位此位,EMAC不允许修改。

Ownership

若置位,表示当下包(从sop到eop)的所有描述符被EMAC拥有。此标志在增加描述符到发送队列之前由软件在sop包描述符上设定。对于单个分片包,sop/eop/OWNER标志同时置位。此标志被EMAC清除,一旦当下包的所有描述符完成之后。注:此标志仅当SOP描述符上有效。

EOQ

若置位,表示有问题的描述符是当下发送通道的发送队列中的上一个描述符,发送器停止。软件应用在增加描述符到发送队列前清楚此标志。当EMAC识别出一个描述符是当下报文的最后一个描述符时,置位此标志。(eop标志置位)没有其它描述符在发送队列。(下一个描述符指针为NULL)

                        AM335x网络分析+KSZ9031分析(uboot中ping不通问题)_第6张图片

                                                                                            图5

                          AM335x网络分析+KSZ9031分析(uboot中ping不通问题)_第7张图片

                                                                                           图6

这些描述符存放在固定的内存中,从参考手册中可以看到这块区域叫做CPPI_RAM,地址是在0x4A102000,这是ti专门为网络模块dma所设置的一块区域,从上面的收发描述符可以看出它是一个链表(每一个描述符占据48个字节,上图中只显示了32个字节,实际上在程序中定义的描述符结构体如下所示)。

struct cpdma_desc {
	/* hardware fields */
    u32	hw_next;
    u32	hw_buffer;
    u32	hw_len;
    u32	hw_mode;
	/* software fields */
    u32	sw_buffer;
    u32	sw_len;
};

 

以ping命令进行情景分析:

  1. Uboot菜单界面执行ping命令实际上调用的是cmd_net.c中的do_ping函数,调用netloop函数,传给netloop的参数决定了你要执行的功能,netloop中一开始有一个大的switch来根据你传入的参数执行相应的功能函数,这里我们执行ping功能,传入的参数就是PING
  2. Switch中ping分支调用的是ping_start()
    ping_start()先设定ping_timeout时间,超出这个时间没有回应就是ping失败
    然后调用ping_send();
  3. ping_send()首先进行icmp头部封装
    然后调用ArpRequest()
    ArpRequest()进行arp封装,然后调用arp_raw_request发送。arp_raw_request最终调用的是cpsw_send函数
  4. 然后就是一个大循环,等待接收包,进行包解析,循环内容如下
    {
    eth_rx();函数接收数据包,
    ArpTimeoutCheck();判断ping_timeou时间有没有到
    然后一个大switch根据net_state这个状态值执行相应操作,net_state状态包括
        NETLOOP_CONTINUE,
        NETLOOP_RESTART,
        NETLOOP_SUCCESS,
        NETLOOP_FAIL
    这些状态在接收包的时候会做出改变,接收到正确的ping返回包即状态变为NETLOOP_SUCCESS,未超时,第一次接收失败,为NETLOOP_CONTINUE,。超时未收到,则NETLOOP_FAIL。(具体详见函数)
    }
  5. 返回失败值或成功值
    到此结束

分析第二步的ping_send(),ping_send调用cpsw_send

static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan,
			 void **buffer, int *len)
{
	struct cpdma_desc *desc = chan->head;
	u32 status;
           return -ENOENT;
 //发送过程只会执行到这,因为它预先没有提交描述符信息,需要直接返回去调用cpdma_submit,从
//CPSW_init中可以看出了注册的时候只注册了接受描述符,发送描述符,是在每一次发送前注册的,原因也很
//简单,你发送的时候总归是知道你要发送了,但是接收你不知道什么时候接受,你得准备好位子去接收。
	      
}

static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan,
			void *buffer, int len)
{
	struct cpdma_desc *desc, *prev;
	u32 mode;
                          u32 status;	
	desc = cpdma_desc_alloc(priv); //分配一个描述符
	if (!desc)
		return -ENOMEM;
	if (len < PKT_MIN)
		len = PKT_MIN;
    status = desc_read(desc, hw_mode);
	mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP;//填充描述符
 	desc_write(desc, hw_next,   0);//填充描述符
	desc_write(desc, hw_buffer, buffer);//填充描述符
	desc_write(desc, hw_len,    len);//填充描述符
	desc_write(desc, hw_mode,   mode | len);//填充描述符
	desc_write(desc, sw_buffer, buffer);//填充描述符
	desc_write(desc, sw_len,    len);//填充描述符
	if (!chan->head) {  //第一个包的话会构建队列
		/* simple case - first packet enqueued */
		chan->head = desc;
		chan->tail = desc;
		chan_write(chan, hdp, desc); //当把描述符地址填入描述符地址指针寄存器的时候这个发送就
//开始了,重点就在这,这个地址在eboot中是虚拟地址,但是一档我们向这个寄存器写入这个地址,后面的传
//输都是自动完成的,而这是个DMA模块,这边的操作必须是实际物理地址,所以必须在这边换成物理地址,但
//是只能改写入寄存器的这部分地址为物理地址,不能影响程序的运行,这里面有些相互关联的地方,要仔细
//的一一改错去。
		goto done;
	}
非第一个包的队列操作
	/* not the first packet - enqueue at the tail */
	prev = chan->tail;
	desc_write(prev, hw_next, desc);
	chan->tail = desc;

	/* next check if EOQ has been triggered already */
	if (desc_read(prev, hw_mode) & CPDMA_DESC_EOQ)
		chan_write(chan, hdp, desc); //当把描述符地址填入描述符地址指针寄存器的时候这个发送就开始了
done:
	if (chan->rxfree) 	
		chan_write(chan, rxfree, 1);
	return 0;
}

AM335X内部机制,当一切初始化好的时候,将描述符地址填入描述符地址指针寄存器,这个时候发送就开始了。

然后我们来看接收部分:

eth_rx();函数接收数据包最终调用的是cpsw_recv

static int cpsw_recv(struct eth_device *dev)
{
while (cpdma_process(priv, &priv->rx_chan, &buffer, &len) >= 0) {//读取状态,有收到包则把数据包读出来
NetReceive(buffer, len);//处理数据包//如果收到的是arp回复包则在里面再送icmp(ping请求)
cpdma_submit(priv, &priv->rx_chan, buffer, PKTSIZE);//构造新的等待接收的接收描述符信息
	}
}

static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan,
			 void **buffer, int *len)
{
	struct cpdma_desc *desc = chan->head;
	u32 status;
           return -ENOENT;
	      //接收描述符是初始化的时候就填好的,描述符中的状态是硬件自动改变的,根据这些状态可以知道接收成功没有,发送成功没有
	status = desc_read(desc, hw_mode);
    //读取描述符中的状态
	if (len)
		*len = status & 0x7ff;
 //读取包长度   
	if (buffer)
        *buffer = desc_read_ptr(desc, sw_buffer);
	//读取数据buffer	
	if (status & CPDMA_DESC_OWNER) { //如果描述符得这一位还是CPDMA_DESC_OWNER说明没有收到数据,该位没有被改变
    		if (chan_read(chan, hdp) == 0) {
			if (desc_read(desc, hw_mode) & CPDMA_DESC_OWNER)
                chan_write(chan, hdp, desc);
		}
		return -EBUSY;
	}
 	chan->head = desc_read_ptr(desc, hw_next);//运行到这说明接收完成,就把下一个描述符放到队列头部
	chan_write(chan, cp, desc); //写入传输完成指针寄存器,表示传输完成
	cpdma_desc_free(priv, desc);
	return 0;
}

总结一下分析网络问题的心路历程:利用wireshark捕捉不到arp包,进行如下分析

  1. 第一步:首先第一个确定mac与外部phy连接是否正常,这点比较好确认,
    mac与phy的配置引脚是mdio(一根时钟线,一根数据线),有点像IIC。但据说比IIC快,具体为什么快,没有研究。
    首先认清楚一点,所有的phy芯片内部的配置寄存器前一部分是国际统一的,是IEEE规定的(如右图IEEE定义的寄存器所示)
    我们要做的就是读写这些寄存器,读写成功了说明你的配置成功的,mac与外部phy连接是正常的,如果读写有问题,那就仔细检查AM335X关于MDIO寄存器的配置。并检查一下phy芯片定义的寄存器,phy初始化完成之后,把phy芯片的寄存器全部读出来,一一比较,看有没有什么配置错误的,有没有打开自动协商功能等等
  2. 第二步:用示波器看看发送引脚有没有数据,有没有时钟信号,如果没有时钟说明配置有误,检查配置寄存器,如果有时钟但是没有数据则确认在发送之前,你的发送数据包里面有没有数据,
    void arp_raw_request(IPaddr_t sourceIP, const uchar *targetEther,
        IPaddr_t targetIP)
    static int cpsw_send(struct eth_device *dev, void *packet, int length)
    static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan,
                void *buffer, int len)
    这三个函数中buffer都打印出来看看,是不是有数据的,没有数据的话就说明函数连接错误,没有连接到cpsw_send。有数据的话就检查这些数据的格式是不是正确的,不同的数据包的格式网上都有现成的可以查,如果发现数据格式不对,大概率是因为大小端实现函数未实现,或者实现有误
    大小端实现函数如下:
     #define htons( value ) ((UINT16)((((UINT16)value) << 8) | (((UINT16)((UINT16)value)) >> 8)))
        #define ntohs( value ) htons( value )
        #define htonl( value ) ((((ULONG)value) << 24) | ((0x0000FF00UL & ((ULONG)value)) << 8) | ((0x00FF0000UL & ((ULONG)value)) >> 8) | (((ULONG)value) >> 24))
        #define ntohl( value ) htonl( value )
  3. 第三步:如果有时钟信号,也有数据信号,则参考ti数据手册,网络部分RGMII或者GMII,你用的哪个就看哪个的时序图,看看时钟有数据之间的先后顺序,时间间隔对不对,如果不对,我用的外部芯片是KSZ9031,它可以提供数据的延时,或者时钟的延时,但是AM335X内部是不支持延时功能的,如果你用的phy芯片不支持,并且你用的是AM335X,而且时序不对,那只能请你换一个phy芯片了。
  4. 第四步:如果发送之前有数据,但是示波器上没有显示,说明没有发的出来,说明往描述符头地址指针寄存器里面写的地址是错误的,这个时候就需要去检查,将在cpdma_submit函数中的chan_write(chan, hdp, desc);之前将desc打印出来,这个值必须是实际物理地址,在0x4A102000到0x4A103FFFF之间,如果不是那需要你做地址转换,但是主要这里面地址不对的话一定是你打开了MMU,这就需要你在改的时候小心一点,最好是在用到这些地址的函数的开头,定义物理地址和虚拟地址,只在该用的地方用物理地址,达到的最终效果就是,描述符链表的每一个next中存的都是物理地址,因为一旦开启接收,整个过程都是全自动的,必须确保dma读到的每一个描述符都是在实地址上,如果你只改了第一个描述符,那你会发现,arp包能够发出去,也能收到arp返回包,也能再发送一个ping包,但是ping返回包就收不到了吗,因为你的描述符链表第二个接收地址不是实际地址。(有效的验证方式是将你描述符中定义的数据地址里的内容打印出来,看看是不是有数据,数据对不对)
  5. 所有的步骤之前都需要你确认,你的发送接收都已经使能了。

                                      AM335x网络分析+KSZ9031分析(uboot中ping不通问题)_第8张图片

                                                                  图7 IEEE定义的phy芯片的寄存器

总而言之,脑子中的思路是:外部能不能收到包->有没有发出去->Phy配置对不对->Cpsw配置对不对->DMA配置对不对->实虚地址对不对->时钟时序对不对。

以下附上我的实虚地址改了哪些地方:我的实际地址 0x4A102000对应的虚拟地址是0xB0502000,这得根据你自己的改。修改以下三个函数就可以了。

static void cpdma_desc_free(struct cpsw_priv *priv, struct cpdma_desc *desc)
{
	unsigned int phsic_desc = 0;
	unsigned int viture = 0;
	struct cpdma_desc *viture_desc = NULL;
	unsigned int *pp = &desc;
	if((*pp >= 0x4A102000)&&(*pp <= 0x4A103FFF))
	{
		phsic_desc = *pp;
		viture = *pp + 0x66400000;
		viture_desc = (struct cpdma_desc *)viture;
	}
		if((*pp >= 0xB0502000)&&(*pp <= 0xB0503FFF))
	{
		phsic_desc = *pp - 0x66400000;
		viture = *pp;
		viture_desc = (struct cpdma_desc *)viture;
	}
	if (desc) {
		desc_write(viture_desc, hw_next, priv->desc_free);
		priv->desc_free = phsic_desc;
	}
}

static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan,
			void *buffer, int len)
{
	struct cpdma_desc *desc, *prev;
	u32 mode;
	int i=0; 
	unsigned char *buf = (unsigned char *)buffer;
	desc = cpdma_desc_alloc(priv);
	unsigned int phsic_desc = 0;
	unsigned int viture = 0;
	struct cpdma_desc *viture_desc = NULL;
	unsigned int *pp = &desc;
	if((*pp >= 0x4A102000)&&(*pp <= 0x4A103FFF))
	{
		phsic_desc = *pp;
		viture = *pp + 0x66400000;
		viture_desc = (struct cpdma_desc *)viture;
	}
		if((*pp >= 0xB0502000)&&(*pp <= 0xB0503FFF))
	{
		phsic_desc = *pp - 0x66400000;
		viture = *pp;
		viture_desc = (struct cpdma_desc *)viture;
	}
	printf("### cpdma_submit  \n");
	if (!desc)
		return -ENOMEM;

	if (len < PKT_MIN)
		len = PKT_MIN;
	for(i=0; ihw_mode, desc->hw_buffer, desc->hw_len, desc->hw_next, desc->sw_buffer, desc->sw_len);
	if (!chan->head) {
		/* simple case - first packet enqueued */
		chan->head = viture_desc;
		chan->tail = viture_desc;
		chan_write(chan, hdp, phsic_desc);
		goto done;
	}

	/* not the first packet - enqueue at the tail */
	prev = chan->tail;
	desc_write(prev, hw_next, phsic_desc);
	chan->tail = viture_desc;

	/* next check if EOQ has been triggered already */
	if (desc_read(prev, hw_mode) & CPDMA_DESC_EOQ)
		chan_write(chan, hdp, phsic_desc);
	printf("### chanhdp %x \n", &(chan->hdp));
done:
	if (chan->rxfree)
		chan_write(chan, rxfree, 1);
	printf("### cpdma_submit \n");
	return 0;
}

static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan,
			 void **buffer, int *len)
{
	struct cpdma_desc *desc = chan->head;
	u32 status;
	unsigned int phsic_desc = 0;
	unsigned int viture = 0;
	struct cpdma_desc *viture_desc = NULL;
	unsigned int *pp = &desc;
	if((*pp >= 0x4A102000)&&(*pp <= 0x4A103FFF))
	{
		phsic_desc = *pp;
		viture = *pp + 0x66400000;
		viture_desc = (struct cpdma_desc *)viture;
	}
		if((*pp >= 0xB0502000)&&(*pp <= 0xB0503FFF))
	{
		phsic_desc = *pp - 0x66400000;
		viture = *pp;
		viture_desc = (struct cpdma_desc *)viture;
	}
	printf("### desc  %x\n", desc);
	if (!desc)
		return -ENOENT;

	status = desc_read(viture_desc, hw_mode);
	printf("### status  %x\n", status);
	if (len)
		*len = status & 0x7ff;

	if (buffer)
		*buffer = desc_read_ptr(viture_desc, sw_buffer);

	if (status & CPDMA_DESC_OWNER) {
		printf("### CPDMA_DESC_OWNER  \n");
		if (chan_read(chan, hdp) == 0) {
			if (desc_read(viture_desc, hw_mode) & CPDMA_DESC_OWNER)
				chan_write(chan, hdp, phsic_desc);
		}

		return -EBUSY;
	}
	printf("### desc2  %x\n", desc);
	printf("### status2  %x\n", status);
	chan->head = desc_read_ptr(viture_desc, hw_next);
	chan_write(chan, cp, phsic_desc);

	cpdma_desc_free(priv, viture_desc);
	return 0;
}

转载请注明出处

https://blog.csdn.net/qq_20753873/article/details/89139365

你可能感兴趣的:(嵌入式linux驱动开发)