98dx166switch交换芯片接口移植(一):SMI接口以及MII接口

一:交换机的原理机制

信号转发的网络设备,介入交换机的任意两个节点共享信号通路,工作与OSI的数据链路层,同事可以进行多个端口的数据传输,交换机上电后会自动创建一个端口地址表,叫做MAC地址表,,会记录mac地址和哪个端口连接,然后自动学习,每次进到交换机的信息,都会记录下穿送过来的设备地址的mac地址,过程就是,学习mac地址,广播mac地址,查找mac地址,配对mac地址。

二:switch交换芯片的基本概念介绍:

port:就是r45接口,vlan:以port为基础定义vlan组,这种vlan用来隔离不同的网络。通过区分802.1Q标签所带的VLAN ID值不同来划分到不同的VLAN组, 一般这种VLAN会与QoS结合起来应用。qos:prot qos,为不同的port定义不同的优先级,differentqos,用IP TOS定义优先级,802.1P  在802.1P标签里定义不同的优先级,可以和802.1Q VLAN结合起来应用。,MACqos,可以根据特定的mac和ip地址定义不同的优先级。

IEEE802.3数据格式:目标地址(6),原地址(6),长度(2),数据(46~1500),fcs(4)

1:接口介绍:

SMI接口:串行管理接口,也被称作MII管理接口,包括MDC和MDIO两条信号线,MDIO用来读写PHY的状态,MDC用来提供时钟,MII用于连接MAC和PHY,包含两种信号,一个接口用于MAC和phy之间的以太网数据,一个用于PHY的管理,读写phy的寄存器大道控制phy的状态,MDIO是双向的,一个mac最多连接32个phy,mac作为主,phy作为从,写phy寄存器时,由mac驱动MDIO向phy写入数据,读phy寄存器时,前半段由mac驱动发送寄存器地址,phy驱动回复寄存器的值。MDC由MAC输出,是非周期性的,不要求提供固定时钟,phy芯片用于输入,上升沿出发MDIO的读写,MDC的时钟频率可以是DC-2.5MHz,即最小的时钟周期为400ns。

MDIO数据格式定义在IEEE 802.3以太网的标准中

Preamble+Start:32bits的前导码以及2bit的开始位。

OP Code:2bits的操作码,10表示读,01表示写。

PHYAD:5bits的PHY地址,一般PHY地址从0开始顺序编号,例如6口switch中PHY地址为0-5。

REGAD:5bits的寄存器地址,即要读或写的寄存器。

Turn Around:2bits的TA,在读命令中,MDIO在此时由MAC驱动改为PHY驱动,并等待一个时钟周期准备发送数据。在写命令中,不需要MDIO方向发生变化,则只是等待两个时钟周期准备写入数据。

Data:16bits数据,在读命令中,PHY芯片将读到的对应PHYAD的REGAD寄存器的数据写到Data中,在写命令中,MAC将要写入对应PHYAD的REGAD寄存器的值写入Data中。

Idle:空闲状态,此时MDIO无源驱动,处高阻状态,但一般用上拉电阻使其处在高电平。

为了确保PHY能准确采样,当MAC向mdio写数据的时候,需要在MDC的上升沿之前就把数据写到MDIO上,

接下来是phy寄存器的介绍:

mii接口包括四个状态,mac与物理层的发送状态,物理层到mac层的接收状态,mac的指示状态,物理层和mac层的物理控制传输信息。

三:基于hisi3536的mdio总线的配置

函数位于:drivers/net/ethernet/stmmac/stmmac_mdio.c中,调用在drivers/net/ethernet/stmmac/stmmac_main.c

stmmac_mdio_register(struct net_device *ndev):用于注册mdio总线。

stmmac_mii_bus = mdiobus_alloc();分配一个mdio总线

  if (priv->phy_addr != -1)
            irqlist[priv->phy_addr] = priv->phy_irq;分配一个IRQ对phy地址。

tnkclk = mdio_clk_init();初始化mdio时钟

 stmmac_mii_bus->read = &stmmac_mdio_read;读数据从phy驱动mii寄存器的读取数据
 stmmac_mii_bus->write = &stmmac_mdio_write;写寄存器
stmmac_mii_bus->reset = &stmmac_mdio_reset;mdio总线的读写复位

stmmac_mii_bus->priv = ndev;总线的私有数据是总线的设备信息

err = mdiobus_register(stmmac_mii_bus);注册一个mdio总线,注册参数包括操作参数,父设备信息,中断列表

if (!driver_for_each_device  (&(stmmacphy_driver.driver), NULL, (void *)priv,stmmac_associate_phy)):搜寻驱动为每个设备,最后将调用stmmac_associate_phy,功能。

stmmac_associate_phy(struct device *dev, void *data):浏览所有的phy,注册和检查是否连接到我们的mac地址,如果是,填充phy配置到我们的本地结构体中。

stmmac_external_phy_reset(i, syscfg_base_ioaddr);复位phy寄存器配置:有两种方式,寄存器复位和gpio配置。

stmmac_syscfg_phy_cfg(priv, SPEED_100, DUPLEX_FULL, 0);对phy的配置以及复位,通过phy的配置对mac寄存器的配置

stmmac_open(struct net_device *dev):在mac设备打开的钩子函数中,stmmac_init_phy(dev);初始化phy驱动,对每个网口调用phy芯片连接。

static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, u16 phydata){
    struct net_device *ndev = bus->priv;
    struct stmmac_priv *priv = netdev_priv(ndev);
    unsigned int mii_address = priv->hw->mii.addr;
    unsigned int mii_data = priv->hw->mii.data;

//16位寄存器

    u16 value = (((phyaddr << 11) & (0x0000F800)) | ((phyreg << 6) & (0x000007C0)))| MII_WRITE;

   value |= MII_BUSY | ((priv->clk_csr & 0xF) << 2);

    /* Wait until any existing MII operation is complete */
    if (stmmac_mdio_busy_wait(priv->ioaddr, mii_address))
        return -EBUSY;

    /* Set the MII address register to write */

//写数据到phy的数据配置寄存器
    writel(phydata, priv->ioaddr + mii_data);

//写地址到phy的地址配置寄存器
    writel(value, priv->ioaddr + mii_address);

    /* Wait until any existing MII operation is complete */
    return stmmac_mdio_busy_wait(priv->ioaddr, mii_address);
}

小知识点:

desc->des2 = dma_map_single(priv->device, skb->data,nopaged_len, DMA_TO_DEVICE);流式dma 映射,驱动需要从别的模块传经来的地址空间作为dma 的缓冲区,那就考虑流式dma映射,初始纳入的地址空间必须是位于内核块的线性区域,驱动的处理主要确保每次的dma操作前后cache的一致性问题。

一致性dma映射:该缓冲区的存在周期与所在的驱动模块一样长,就用一致性dma映射,这种映射在一开始为dma 操作分区解决了cache一致性的问题。map_page

    priv->dma_rx =  (struct dma_desc *)dma_alloc_coherent(priv->device,   rxsize *  sizeof(struct dma_desc),&priv->dma_rx_phy, GFP_KERNEL);

  priv->dma_rx内核的虚拟起始地址,内核调用此地址,&priv->dma_rx_phy,返回的内核的物理起始地址。

 

四:mac接口配置

static const struct stmmac_ops dwmac1000_ops = {
    .core_init = dwmac1000_core_init, 初始化,一般用来配置mac中断
    .rx_coe = dwmac1000_rx_coe_supported,支持校验分流管理
    .dump_regs = dwmac1000_dump_regs, 打印mac寄存器基地址
    .host_irq_status = dwmac1000_irq_status, intr_status = readl(ioaddr + GMAC_INT_STATUS);读取中断状态,主要发送接收中断状态
    .set_filter = dwmac1000_set_filter,多播缓冲设置
    .flow_ctrl = dwmac1000_flow_ctrl, writel(flow, ioaddr + GMAC_FLOW_CTRL);流控制设置
    .pmt = dwmac1000_pmt,电压管理模式
    .set_umac_addr = dwmac1000_set_umac_addr,设置mac 地址
    .get_umac_addr = dwmac1000_get_umac_addr,
};

writel(0x60f, ioaddr + GMAC_INT_MASK);标记mac中断

有一个设备配置结构体struct mac_device_info mac。

mac的连接设置

    mac->link.port = GMAC_CONTROL_PS;
    mac->link.duplex = GMAC_CONTROL_DM;
    mac->link.speed = GMAC_CONTROL_FES;
    mac->mii.addr = GMAC_MII_ADDR; //对应的mii的偏移地址
    mac->mii.data = GMAC_MII_DATA; //对应的mii的偏移数据

 

五:基于mac的dma 操作

const struct stmmac_dma_ops dwmac1000_dma_ops = {
    .init = dwmac1000_dma_init,dma 初始化,axi总线初始化,dma中断初始化,发送接收源初始化
    .dump_regs = dwmac1000_dump_dma_regs,打印寄存器
    .dma_mode = dwmac1000_dma_operation_mode,dma模式初始化
    .dma_diagnostic_fr = dwmac1000_dma_diagnostic_fr,
    .enable_dma_transmission = dwmac_enable_dma_transmission,使能dma发送
    .enable_dma_receive = dwmac_enable_dma_receive,使能dma 接收writel(1, ioaddr + (channel * 0x100) + DMA_XMT_POLL_DEMAND);根据通道号选择。
    .get_dma_rx_state = dwmac_get_dma_rx_state,读取dma通道的接收状态
    .enable_dma_irq = dwmac_enable_dma_irq, 使能中断
    .disable_dma_irq = dwmac_disable_dma_irq,
    .start_tx = dwmac_dma_start_tx, 开始发送
    .stop_tx = dwmac_dma_stop_tx,
    .start_rx = dwmac_dma_start_rx,开始接收
    .stop_rx = dwmac_dma_stop_rx,
    .dma_interrupt = dwmac_dma_interrupt, intr_status = readl(ioaddr + (channel * 0x100) + DMA_STATUS);读取中断状态,根据中断状态操作
};

六:增强型设备驱动结构体:在程序中实际调用的发送接收接口函数,就饿是dma的上层接口函数。

const struct stmmac_desc_ops enh_desc_ops = {
    .tx_status = enh_desc_get_tx_status,根据dma 的状态返回函数接口的接收状态
    .rx_status = enh_desc_get_rx_status,
    .get_tx_len = enh_desc_get_tx_len,
    .init_rx_desc = enh_desc_init_rx_desc,
    .init_tx_desc = enh_desc_init_tx_desc,
    .get_tx_owner = enh_desc_get_tx_owner,
    .get_rx_owner = enh_desc_get_rx_owner,
    .release_tx_desc = enh_desc_release_tx_desc,
    .prepare_tx_desc = enh_desc_prepare_tx_desc,
    .clear_tx_ic = enh_desc_clear_tx_ic,
    .close_tx_desc = enh_desc_close_tx_desc,
    .get_tx_ls = enh_desc_get_tx_ls,
    .set_tx_owner = enh_desc_set_tx_owner,
    .set_rx_owner = enh_desc_set_rx_owner,
    .get_rx_frame_len = enh_desc_get_rx_frame_len,
    .get_rx_dirty_flag = enh_desc_get_rx_dirty_flag,
    .set_rx_dirty_flag = enh_desc_set_rx_dirty_flag,
    .clear_rx_dirty_flag = enh_desc_clear_rx_dirty_flag,
    .get_rx_curr_flag = enh_desc_get_rx_curr_flag,
    .set_rx_curr_flag = enh_desc_set_rx_curr_flag,
    .clear_rx_curr_flag = enh_desc_clear_rx_curr_flag,
    .set_tx_igmp  = enh_desc_set_tx_igmp,
    .clean_tx_igmp  = enh_desc_clean_tx_igmp,
};

七:数据接收包:

当产生mac中断后调用该函数static int stmmac_poll(struct napi_struct *napi, int budget)

        status = priv->hw->desc->rx_status(&priv->dev->stats,&priv->xstats, p);读取接收帧状态

八:基于phy的连接:通过调用

stmmac_open(struct net_device *dev);打开网络设备,stmmac_init_phy(dev);初始化phy

phydev = phy_connect(dev, phy_id, &stmmac_adjust_link,priv->phy_interface);连接一个phy设备到网络驱动

    d = bus_find_device_by_name(&mdio_bus_type, NULL, bus_id);通过总线名称查找注册到总线上的phy设备

rc = phy_connect_direct(dev, phydev, handler, interface);连接一个phy设备到网络驱动上,

stmmac_adjust_link(struct net_device *dev):当phy的状态发生变化,通过这个函数改变,修改mac寄存器,修改phy的寄存器,配置速率,控制方式,连接状态。

九:所用的设备驱动:/driver/net/phy、phy_device.c文档中创建phy的驱动

static struct phy_driver genphy_driver = {
    .phy_id        = 0xffffffff,
    .phy_id_mask    = 0xffffffff,
    .name        = "Generic PHY",phy驱动名称
    .config_init    = genphy_config_init,  //配置初始化
    .features    = 0,
    .config_aneg    = genphy_config_aneg, //自动配置参数
    .read_status    = genphy_read_status, //读取phy的状态。连接状态,双工,自动配置等功能
    .suspend    = genphy_suspend,  //低功率使能
    .resume        = genphy_resume,
    .driver        = {.owner= THIS_MODULE, },
};

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值