全志D1开发(十三)网络驱动之网络硬件MAC初始化

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2022年5月3日20:54:27 评论 793

网络MAC控制器的初始化流程和SD控制器其实类似,大致分为以下几步:

  • 引脚复用:老生常谈的东西,在使用MAC功能之前需要将引脚复用设置正确,由于本次我们采用的uboot自带网络功能,已经将引脚复用初始化好了,所以这个不需要再次设置。
  • 初始化时钟:设置ccu寄存器使能MAC控制器的时钟源。
  • 复位和初始化MAC控制器:由于网络是使用的DMA描述符来传输数据,所以我们还需要设置DMA描述符内存和缓冲区。

时钟源使能和SD类似,我们在ccu中添加一个 ccu_emac_bus_gate_enable 接口:

void ccu_emac_bus_gate_enable (int enable)
{
    u32 val;
    val = readl(CCU_REG_EMAC_BUS);

    if (enable) {
        // de assert emac
        writel(val | (1 << 16), CCU_REG_EMAC_BUS);
        usleep(100);
        // emac gate pass
        writel(val | (1 << 0), CCU_REG_EMAC_BUS);
    } else {
        // emac gate mask
        writel(val & (~(1 << 0)), CCU_REG_EMAC_BUS);
    }
}

MAC控制器初始化中有一个比较重要的操作就是发送和接收DMA描述符及缓冲区的设置。网络收发的数据需要存放在DMA内存中,而用来描述这些数据的描述符本身也要占用DMA内存,所以我们使用 API_VmmDmaAlloc 接口来申请DMA内存区内存,通过这个接口申请出来的内存是不带cache的,所以避免了cache一致性问题。

我们将收发描述符各自初始化使用512个,一般情况下这足够了,每个描述符对应的buffer设置为和uboot下驱动中一样的大小2044。接收描述符初始化时需要将描述符的所有者设置为DMA硬件所有,因为接收是异步的,所以还需要在中断中进行接收数据的处理, 本网卡驱动中,发送不使用中断。DMA描述符的设置如下所示:

static void rx_descs_init(struct emac_eth_dev *emac)
{
    struct emac_dma_desc *desc_table_p;
    char *rxbuffs;
    struct emac_dma_desc *desc_p;
    int i;

    desc_table_p = emac->rx_chain = API_VmmDmaAlloc(CONFIG_RX_DESCR_NUM * sizeof(struct emac_dma_desc));
    rxbuffs = emac->rxbuffer = API_VmmDmaAlloc(RX_TOTAL_BUFSIZE);

    bzero(desc_table_p, 0);
    bzero(rxbuffs, 0);

    for (i = 0; i < CONFIG_RX_DESCR_NUM; i++) {
        desc_p = &desc_table_p[i];
        desc_p->buf_addr = (uintptr_t)&rxbuffs[i * CONFIG_ETH_BUFSIZE];
        desc_p->next = (uintptr_t)&desc_table_p[i + 1];
        desc_p->ctl_size = CONFIG_ETH_RXSIZE;
        desc_p->status = EMAC_DESC_OWN_DMA;
    }

    /* Correcting the last pointer of the chain */
    desc_p->next = (uintptr_t)&desc_table_p[0];

    writel((uintptr_t)&desc_table_p[0], (emac->mac_reg + EMAC_RX_DMA_DESC));
    emac->rx_currdesc = 0;
}

static void tx_descs_init(struct emac_eth_dev *emac)
{
    struct emac_dma_desc *desc_table_p;
    char *txbuffs;
    struct emac_dma_desc *desc_p;
    int i;

    desc_table_p = emac->tx_chain = API_VmmDmaAlloc(CONFIG_TX_DESCR_NUM * sizeof(struct emac_dma_desc));
    txbuffs = emac->txbuffer = API_VmmDmaAlloc(TX_TOTAL_BUFSIZE);

    for (i = 0; i < CONFIG_TX_DESCR_NUM; i++) {
        desc_p = &desc_table_p[i];
        desc_p->buf_addr = (uintptr_t)&txbuffs[i * CONFIG_ETH_BUFSIZE];
        desc_p->next = (uintptr_t)&desc_table_p[i + 1];
        desc_p->ctl_size = 0;
        desc_p->status = 0;
    }

    /* Correcting the last pointer of the chain */
    desc_p->next =  (uintptr_t)&desc_table_p[0];

    writel((uintptr_t)&desc_table_p[0], emac->mac_reg + EMAC_TX_DMA_DESC);
    emac->tx_currdesc = 0;
}

发送描述符的大小和所有者在实际发送函数中会去设置,DMA描述符初始化好之后就将描述符基址设置到相应的寄存器中, 同时设置其他寄存器使能DMA功能,最后对外封装出 emacInit 接口使用:

int emacInit(struct netdev *pnetdev)
{
    struct emac_eth_dev *emac = &emac_dev;

    emac->mac_reg    = (void *)EMAC_REG_BASE;
    emac->sysctl_reg = (void *)SYS_CFG_REG_BASE ;

    // TODO:初始化引脚复用

    // TODO:初始化ccu时钟和复位
    ccu_emac_bus_gate_enable(1);
    sun8i_emac_set_syscon(emac);

    // 初始化mdio和phy
    sun8i_mdio_init(pnetdev);
    sun8i_emac_eth_init(pnetdev, emac);

    return 0;
}

这其中还有其他一些系统寄存器需要去设置,大家自行对照手册和源码进行深入分析,这里就不展开了。

gewenbin
  • 本文由 发表于 2022年5月3日20:54:27
  • 转载请务必保留本文链接:http://www.databusworld.cn/10722.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: