在Xilinx zynq7020平台使用Marvell6020交换机芯片

初次写博客,不好的地方欢迎提出建议。

目    标    :在ZYNQ7020处理器的linux平台调通Marvell 88e6020交换机,实现局域网内可以通讯。

硬件平台:zynq7020(ARM CORTEX-A9),Marvell 88E6020交换机芯片

内核版本:linux4.14.0

                 

       说到网络,肯定是先想到网络OSI7层协议模型,直接度娘可以找到很多7层模型的详细介绍,以下介绍简单介绍相关的MAC层和PHY层。

•MAC是Media Access Control的缩写,即媒体访问控制子层协议。该协议位于OSI七层协议中数据链路层的下半部分,主要负责控制与连接物理层的物理介质

  • 在发送数据的时候,MAC协议可以事先判断是否可以发送数据,如果可以发送将给数据加上一些控制信息,最终将数据以及控制信息以规定的格式发送到物理层;

  • 在接收数据的时候,MAC协议首先判断输入的信息并是否发生传输错误,如果没有错误,则去掉控制信息发送至LLC层。以太网MAC由IEEE-802.3以太网标准定义。

•PHY是物理接口收发器,它实现物理层。包括MII/GMII(介质独立接口)子层、PCS(物理编码子层)、PMA(物理介质附加)子层、PMD(物理介质相关)子层、MDI子层。

  • PHY在发送数据时,收到MAC过来的数据(对PHY来说,没有帧的概念,对它来说,都是数据而不管什么地址,数据还是CRC),每4bit就增加1bit的检错码,然后把并行数据转化为串行流数据,再按照物理层的编码规则把数据编码,再变为模拟信号把数据送出去

  • 收数据时的流程反之。

PHY与MAC的通迅接口

MAC和PHY是通过MII(Medium Independent Interface(介质独立接口))进行通信。包括如下:

  • 数据接口,有TX/RX两条独立的通道。
  • 管理接口(SMI),由时钟信号和数据信号组成,控制监控PHY的工作状态。

以MII的基础,又有了以下多种数据接口:

  1. RMII(Reduced Media Independant Interface),简化的MII,信号线更少了。
  2. GMII(Gigabit Media Independent Interface)千兆的MII接口。
  3. RGMII (Reduced Gigabit Media Independent Interface)简化的千兆接口。

SMI(MII管理接口):

  • 串行管理接口(Serial Management Interface)也被称作MII管理接口(MII Management Interface),包括MDC和MDIO两条信号线。MDIO是一个PHY的管理接口,用来读/写PHY的寄存器,以控制PHY的行为或获取PHY的状态,MDC为MDIO提供时钟。

这次的硬件使用的是RGMII接口:

  • RGMII均采用4位数据接口,工作时钟125MHz,并且在上升沿和下降沿同时传输数据,因此传输速率可达1000Mbps。 

PHY层之下是RJ45,RJ45就是我们平常使用的网线水晶头插线口。

以下为MAC和PHY通讯的整体框图

       

以下为研究代码过程

ZYNQ7020 MAC驱动

  • 在源码包的位置   source_root/drivers/net/ethernet/cadence/

  • drivers/net/ethernet/cadence/macb_main.c  为MAC驱动代码。

macb_main.c 

  • 平台注册到probe执行,在probe中执行了macb_mii_init函数。
/*平台注册*/
static struct platform_driver macb_driver = {
    .probe      = macb_probe,
    .remove     = macb_remove,
    .driver     = {
        .name       = "macb",
        .of_match_table = of_match_ptr(macb_dt_ids),
        .pm = &macb_pm_ops,
    },
};

module_platform_driver(macb_driver);


/*macb_probe*/
static int macb_probe(struct platform_device *pdev)
{
    int (*clk_init)(struct platform_device *, struct clk **,
            struct clk **, struct clk **,  struct clk **,
            struct clk **) = macb_clk_init;
    int (*init)(struct platform_device *) = macb_init;
    struct device_node *np = pdev->dev.of_node;
    struct device_node *phy_node;
    const struct macb_config *macb_config = NULL;
    struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL;
    struct clk *tsu_clk = NULL;
    unsigned int queue_mask, num_queues;
    struct macb_platform_data *pdata;
    bool native_io;
    struct phy_device *phydev;
    struct net_device *dev;
    struct resource *regs;
    void __iomem *mem;
    const char *mac;
    struct macb *bp;
    int err;

    regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    mem = devm_ioremap_resource(&pdev->dev, regs);
    if (IS_ERR(mem))
        return PTR_ERR(mem);
    if (np) {
        const struct of_device_id *match;

        match = of_match_node(macb_dt_ids, np);
        if (match && match->data) {
            macb_config = match->data;
            clk_init = macb_config->clk_init;
            init = macb_config->init;
        }
    }

    err = clk_init(pdev, &pclk, &hclk, &tx_clk, &rx_clk, &tsu_clk);
    if (err)
        return err;

    pm_runtime_set_autosuspend_delay(&pdev->dev, MACB_PM_TIMEOUT);
    pm_runtime_use_autosuspend(&pdev->dev);
    pm_runtime_get_noresume(&pdev->dev);
    pm_runtime_set_active(&pdev->dev);
    pm_runtime_enable(&pdev->dev);
    native_io = hw_is_native_io(mem);

    macb_probe_queues(mem, native_io, &queue_mask, &num_queues);
    dev = alloc_etherdev_mq(sizeof(*bp), num_queues);
    if (!dev) {
        err = -ENOMEM;
        goto err_disable_clocks;
    }

    dev->base_addr = regs->start;

    SET_NETDEV_DEV(dev, &pdev->dev);

    bp = netdev_priv(dev);
    bp->pdev = pdev;
    bp->dev = dev;
    bp->regs = mem;
    bp->native_io = native_io;
    if (native_io) {
        bp->macb_reg_readl = hw_readl_native;
        bp->macb_reg_writel = hw_writel_native;
    } else {
        bp->macb_reg_readl = hw_readl;
        bp->macb_reg_writel = hw_writel;
    }
    bp->num_queues = num_queues;
    bp->queue_mask = queue_mask;
    if (macb_config)
        bp->dma_burst_length = macb_config->dma_burst_length;
    bp->pclk = pclk;
    bp->hclk = hclk;
    bp->tx_clk = tx_clk;
    bp->rx_clk = rx_clk;
    bp->tsu_clk = tsu_clk;
    if (tsu_clk)
        bp->tsu_rate = clk_get_rate(tsu_clk);

    if (macb_config)
        bp->jumbo_max_len = macb_config->jumbo_max_len;

    spin_lock_init(&bp->lock);

    /* setup capabilities */
    macb_configure_caps(bp, macb_config);

#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
    if (GEM_BFEXT(DAW64, gem_readl(bp, DCFG6))) {
        dma_set_mask(&pdev->dev, DMA_BIT_MASK(44));
        bp->hw_dma_cap |= HW_DMA_CAP_64B;
    }
#endif
    platform_set_drvdata(pdev, dev);
 dev->irq = platform_get_irq(pdev, 0);
    if (dev->irq < 0) {
        err = dev->irq;
        goto err_out_free_netdev;
    }

    /* MTU range: 68 - 1500 or 10240 */
    dev->min_mtu = GEM_MTU_MIN_SIZE;
    if (bp->caps & MACB_CAPS_JUMBO)
        dev->max_mtu = gem_readl(bp, JML) - ETH_HLEN - ETH_FCS_LEN;
    else
        dev->max_mtu = ETH_DATA_LEN;

    mac = of_get_mac_address(np);
    if (mac)
        ether_addr_copy(bp->dev->dev_addr, mac);
    else
        macb_get_hwaddr(bp);

    /* Power up the PHY if there is a GPIO reset */
    phy_node = of_parse_phandle(np, "phy-handle", 0);
    if (!phy_node && of_phy_is_fixed_link(np)) {
        err = of_phy_register_fixed_link(np);
        if (err < 0) {
            dev_err(&pdev->dev, "broken fixed-link specification");
            goto failed_phy;
        }
        phy_node = of_node_get(np);
        bp->phy_node = phy_node;
     } else {
        int gpio = of_get_named_gpio(phy_node, "reset-gpios", 0);
        if (gpio_is_valid(gpio)) {
            bp->reset_gpio = gpio_to_desc(gpio);
            gpiod_direction_output(bp->reset_gpio, 1);
        }
    }

    err = of_get_phy_mode(np);
    if (err < 0) {
        pdata = dev_get_platdata(&pdev->dev);
        if (pdata && pdata->is_rmii)
            bp->phy_interface = PHY_INTERFACE_MODE_RMII;
        else
            bp->phy_interface = PHY_INTERFACE_MODE_MII;
    } else {
        bp->phy_interface = err;
    }

    macb_reset_phy(pdev);

    /* IP specific init */
    err = init(pdev);
    if (err)
        goto err_out_free_netdev;
    err = register_netdev(dev);
    if (err) {
        dev_err(&pdev->dev, "Cannot register net device, aborting.\n");
        goto err_out_unregister_netdev;
    }

    err = macb_mii_init(bp);          /*此处为PHY初始化,匹配PHY的过程*/
    if (err)
        goto err_out_unregister_netdev;

    netif_carrier_off(dev);

    tasklet_init(&bp->hresp_err_tasklet, macb_hresp_error_task,
             (unsigned long)bp);

    if (bp->caps & MACB_CAPS_WOL)
        device_set_wakeup_capable(&bp->dev->dev, 1);

    netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n",
            macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID),
            dev->base_addr, dev->irq, dev->dev_addr);

    phydev = bp->phy_dev;
    phy_attached_info(phydev);
    pm_runtime_mark_last_busy(&bp->pdev->dev);
    pm_runtime_put_autosuspend(&bp->pdev->dev);
    return 0;

err_out_unregister_netdev:
    unregister_netdev(dev);

err_out_free_netdev:
    free_netdev(dev);

failed_phy:
    of_node_put(phy_node);

err_disable_clocks:
    clk_disable_unprepare(tx_clk);
    clk_disable_unprepare(hclk);
    clk_disable_unprepare(pclk);
    clk_disable_unprepare(rx_clk);
    clk_disable_unprepare(tsu_clk);
    pm_runtime_disable(&pdev->dev);
    pm_runtime_set_suspended(&pdev->dev);
    pm_runtime_dont_use_autosuspend(&pdev->dev);

    return err;
}

  • macb_mii_init函数为初始化SMI总线,分配空间,封装SMI读写PHY寄存器函数,然后通过MDIO总线去扫描物理连接的PHY,匹配驱动,代码追踪如下。
static int macb_mii_init(struct macb *bp)
{
    struct macb_platform_data *pdata;
    struct device_node *np, *mdio_np;
    int err = -ENXIO, i;

    /* Enable management port */
    macb_writel(bp, NCR, MACB_BIT(MPE));
    /* ***分配空间*****/
    bp->mii_bus = mdiobus_alloc();
    if (!bp->mii_bus) {
        err = -ENOMEM;
        goto err_out;
    }

    bp->mii_bus->name = "MACB_mii_bus";
    /****封装读写PHY寄存器函数****/
    bp->mii_bus->read = &macb_mdio_read;
    bp->mii_bus->write = &macb_mdio_write;
//  bp->mii_bus->read = &mdio_phy_read;
//  bp->mii_bus->write = &mdio_phy_write;
    snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
         bp->pdev->name, bp->pdev->id);
    bp->mii_bus->priv = bp;
    bp->mii_bus->parent = &bp->dev->dev;
    pdata = dev_get_platdata(&bp->pdev->dev);

    dev_set_drvdata(&bp->dev->dev, bp->mii_bus);

    np = bp->pdev->dev.of_node;
    mdio_np = of_get_child_by_name(np, "mdio");
    if (mdio_np) {
        of_node_put(mdio_np);
        err = of_mdiobus_register(bp->mii_bus, mdio_np);
        if (err)
            goto err_out_unregister_bus;
    } else if (np) {
        /* try dt phy registration */
        err = of_mdiobus_register(bp->mii_bus, np);

        /* fallback to standard phy registration if no phy were
         * found during dt phy registration
         */
        if (!err && !phy_find_first(bp->mii_bus)) {
            for (i = 1; i < 2; i++) {
                struct phy_device *phydev;
    /*********扫描物理连接的PHY********/
                phydev = mdiobus_scan(bp->mii_bus, i);
                if (IS_ERR(phydev) &&
                    PTR_ERR(phydev) != -ENODEV) {
                    err = PTR_ERR(phydev);
                    break;
                }
            }

            if (err)
                goto err_out_unregister_bus;
        }
    } else {
        if (pdata)
            bp->mii_bus->phy_mask = pdata->phy_mask;

        err = mdiobus_register(bp->mii_bus);
    }

    if (err)
        goto err_out_free_mdiobus;

    err = macb_mii_probe(bp->dev);
    if (err)
        goto err_out_unregister_bus;

    return 0;

err_out_unregister_bus:
    mdiobus_unregister(bp->mii_bus);
err_out_free_mdiobus:
    mdiobus_free(bp->mii_bus);
err_out:
    return err;
}


  • 执行mdiobus_scan去扫描MDIO总线上挂载的PHY设备。
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
    struct phy_device *phydev;
    int err;

    /*通过设备ID号拿到设备的实例结构体*/
    phydev = get_phy_device(bus, addr, false);
    if (IS_ERR(phydev))
        return phydev;

    /*
     * For DT, see if the auto-probed phy has a correspoding child
     * in the bus node, and set the of_node pointer in this case.
     */
    of_mdiobus_link_mdiodev(bus, &phydev->mdio);
    /*注册PHY设备*/
    err = phy_device_register(phydev);
    if (err) {
        phy_device_free(phydev);
        return ERR_PTR(-ENODEV);
    }

    return phydev;
}
  • 执行get_phy_device通过读取设备的ID号去拿到设备的驱动
struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
    struct phy_c45_device_ids c45_ids = {0};
    u32 phy_id = 0;
    int r;

    r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids);
    if (r)
        return ERR_PTR(r);
    /* If the phy_id is mostly Fs, there is no device there */

    if ((phy_id & 0x1fffffff) == 0x1fffffff)
        return ERR_PTR(-ENODEV);
    return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
}
  • 执行phy_device_register 注册PHY设备
int phy_device_register(struct phy_device *phydev)
{
    int err;
    //注册到MDIO总线上
    err = mdiobus_register_device(&phydev->mdio);
    if (err)
        return err;

    /* Run all of the fixups for this PHY */
    err = phy_scan_fixups(phydev);
    if (err) {
        pr_err("PHY %d failed to initialize\n", phydev->mdio.addr);
        goto out;
    }

    phydev->mdio.dev.groups = phy_dev_groups;

    err = device_add(&phydev->mdio.dev);
    if (err) {
        pr_err("PHY %d failed to add\n", phydev->mdio.addr);
        goto out;
    }

    return 0;

 out:
    mdiobus_unregister_device(&phydev->mdio);
    return err;
}

MDIO总线的实现和目的

目的:总线涉及驱动和设备,匹配注册在总线上的驱动和设备来执行驱动。上面的MAC驱动中执行了设备注册过程,这里讲解MDIO总线,最后面讲解驱动的注册过程。

实现:drivers/net/phy/mdio_bus.c

总线通过match去匹配设备和驱动。

struct bus_type mdio_bus_type = {
    .name       = "mdio_bus",
    .match      = mdio_bus_match,
    .uevent     = mdio_uevent,
    .pm     = MDIO_BUS_PM_OPS,
};

PHY6020驱动

查看数据手册查找设备88e6020 ID为0x01410db0,通过phy_drivers_register函数注册驱动,驱动和设备匹配上后会执行config_init函数。

static struct phy_driver marvell_drivers[] = {
    {
        .phy_id = 0x01410db0,
        .phy_id_mask = 0x0fffffff,
        .name = "Marvell 88E6020",
        .features = PHY_100BT_FEATURES|PHY_10BT_FEATURES|SUPPORTED_MII |
                        SUPPORTED_AUI | SUPPORTED_FIBRE |SUPPORTED_BNC,
        .flags = PHY_HAS_INTERRUPT,
        .config_init = &m88e6020_config_init,
        .config_aneg = &m88e6020_config_aneg,
        .read_status = &m88e6020_genphy_read_status,
    },
};

static int __init mv88e6020_init(void)
{
    int ret = 0;
    mv88e6020_phydev = NULL;
    ret = phy_drivers_register(marvell_drivers,
        ARRAY_SIZE(marvell_drivers),THIS_MODULE);

    if ( ret )
        goto err;

    swicth_to_cpu_link_status = 0;
err:
    return ret;
}

参考的PHY驱动详解网址

linux 的总线,设备,驱动三者关系

MII/MDIO详解

这篇文章追的代码较深,如有不懂可以留下留言。如果想追更深的代码,也可以留言,带你追。。。。

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值