hns_mdio通过注册mdio bus对phy寄存器操作

来源:互联网 发布:网络直播的心理论文 编辑:程序博客网 时间:2024/06/13 11:20
从drivers/net/ethernet/hisilicon 下的Makefile可以看到hns_mido.c 可以被buildin,也可以被build成ko。且只有一个文件hns_mdio.c
obj-$(CONFIG_HNS_MDIO) += hns_mdio.o
static struct platform_driver hns_mdio_driver = {
    .probe = hns_mdio_probe,
    .remove = hns_mdio_remove,
    .driver = {
           .name = MDIO_DRV_NAME,
           .of_match_table = hns_mdio_match,
           .acpi_match_table = ACPI_PTR(hns_mdio_acpi_match),
           },
};

module_platform_driver(hns_mdio_driver);
从人口函数可以看到hns_mdio_driver 可以通过是匹配acpi和dt的方式
static int hns_mdio_probe(struct platform_device *pdev)
{
    struct hns_mdio_device *mdio_dev;
    struct mii_bus *new_bus;
    struct resource *res;
    int ret = -ENODEV;

    if (!pdev) {
        dev_err(NULL, "pdev is NULL!\r\n");
        return -ENODEV;
    }
// 申请一个mdio_dev 这个是厂商私有的结构,后面后通过new_bus->priv = mdio_dev; 来保存这个结构.
    mdio_dev = devm_kzalloc(&pdev->dev, sizeof(*mdio_dev), GFP_KERNEL);
    if (!mdio_dev)
        return -ENOMEM;
//申请一个struct mii_bus *new_bus; 结构,mii_bus 是对所有ethernet的phy driver都要用到的结构
    new_bus = devm_mdiobus_alloc(&pdev->dev);
    if (!new_bus) {
        dev_err(&pdev->dev, "mdiobus_alloc fail!\n");
        return -ENOMEM;
    }
//给new_bus 赋值。read/reset/write 用于控制phy register
    new_bus->name = MDIO_BUS_NAME;
    new_bus->read = hns_mdio_read;
    new_bus->write = hns_mdio_write;
    new_bus->reset = hns_mdio_reset;
    new_bus->priv = mdio_dev;
    new_bus->parent = &pdev->dev;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    mdio_dev->vbase = devm_ioremap_resource(&pdev->dev, res);
    if (IS_ERR(mdio_dev->vbase)) {
        ret = PTR_ERR(mdio_dev->vbase);
        return ret;
    }
//将new_bus 设定为这个device的的drvdata
    platform_set_drvdata(pdev, new_bus);
    snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%s", "Mii",
         dev_name(&pdev->dev));
//判断是dt还是acpi.
    if (dev_of_node(&pdev->dev)) {
        struct of_phandle_args reg_args;

        ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
                               "subctrl-vbase",
                               4,
                               0,
                               &reg_args);
        if (!ret) {
            mdio_dev->subctrl_vbase =
                syscon_node_to_regmap(reg_args.np);
            if (IS_ERR(mdio_dev->subctrl_vbase)) {
                dev_warn(&pdev->dev, "syscon_node_to_regmap error\n");
                mdio_dev->subctrl_vbase = NULL;
            } else {
                if (reg_args.args_count == 4) {
                    mdio_dev->sc_reg.mdio_clk_en =
                        (u16)reg_args.args[0];
                    mdio_dev->sc_reg.mdio_clk_dis =
                        (u16)reg_args.args[0] + 4;
                    mdio_dev->sc_reg.mdio_reset_req =
                        (u16)reg_args.args[1];
                    mdio_dev->sc_reg.mdio_reset_dreq =
                        (u16)reg_args.args[1] + 4;
                    mdio_dev->sc_reg.mdio_clk_st =
                        (u16)reg_args.args[2];
                    mdio_dev->sc_reg.mdio_reset_st =
                        (u16)reg_args.args[3];
                } else {
                    /* for compatible */
                    mdio_dev->sc_reg.mdio_clk_en =
                        MDIO_SC_CLK_EN;
                    mdio_dev->sc_reg.mdio_clk_dis =
                        MDIO_SC_CLK_DIS;
                    mdio_dev->sc_reg.mdio_reset_req =
                        MDIO_SC_RESET_REQ;
                    mdio_dev->sc_reg.mdio_reset_dreq =
                        MDIO_SC_RESET_DREQ;
                    mdio_dev->sc_reg.mdio_clk_st =
                        MDIO_SC_CLK_ST;
                    mdio_dev->sc_reg.mdio_reset_st =
                        MDIO_SC_RESET_ST;
                }
            }
        } else {
            dev_warn(&pdev->dev, "find syscon ret = %#x\n", ret);
            mdio_dev->subctrl_vbase = NULL;
        }

        ret = of_mdiobus_register(new_bus, pdev->dev.of_node);
    } else if (is_acpi_node(pdev->dev.fwnode)) {
        /* Clear all the IRQ properties */
        memset(new_bus->irq, PHY_POLL, 4 * PHY_MAX_ADDR);

        /* Mask out all PHYs from auto probing. */
        new_bus->phy_mask = ~0;
// 注册MDIO bus
        /* Register the MDIO bus */
        ret = mdiobus_register(new_bus);
    } else {
        dev_err(&pdev->dev, "Can not get cfg data from DT or ACPI\n");
        ret = -ENXIO;
    }

    if (ret) {
        dev_err(&pdev->dev, "Cannot register as MDIO bus!\n");
        platform_set_drvdata(pdev, NULL);
        return ret;
    }

    return 0;
}
这里以hns_mdio_read函数为例
static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
{
    int ret;
    u16 reg_val = 0;
    u8 devad = ((regnum >> 16) & 0x1f);
    u8 is_c45 = !!(regnum & MII_ADDR_C45);
    u16 reg = (u16)(regnum & 0xffff);
    struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv;

    dev_dbg(&bus->dev, "mdio read %s,base is %p\n",
        bus->id, mdio_dev->vbase);
    dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x!\n",
        phy_id, is_c45, devad, reg);

    /* Step 1: wait for ready */
//通过读寄存器判断是否可以读
    ret = hns_mdio_wait_ready(bus);
    if (ret) {
        dev_err(&bus->dev, "MDIO bus is busy\n");
        return ret;
    }
如果不是is_c45的话,比较简单,直接写MDIO_C22_READ ,就可以读到寄存器.如果是is_c45的话,则要按照一定的flow才能读到phy寄存器
    if (!is_c45) {
        hns_mdio_cmd_write(mdio_dev, is_c45,
                   MDIO_C22_READ, phy_id, reg);
    } else {
        MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M,
                   MDIO_ADDR_DATA_S, reg);

        /* Step 2; config the cmd-reg to write addr*/
        hns_mdio_cmd_write(mdio_dev, is_c45,
                   MDIO_C45_WRITE_ADDR, phy_id, devad);

        /* Step 3: check for read or write opt is finished */
        ret = hns_mdio_wait_ready(bus);
        if (ret) {
            dev_err(&bus->dev, "MDIO bus is busy\n");
            return ret;
        }

        hns_mdio_cmd_write(mdio_dev, is_c45,
                   MDIO_C45_WRITE_ADDR, phy_id, devad);
    }

    /* Step 5: waitting for MDIO_COMMAND_REG 's mdio_start==0,*/
    /* check for read or write opt is finished */
    ret = hns_mdio_wait_ready(bus);
    if (ret) {
        dev_err(&bus->dev, "MDIO bus is busy\n");
        return ret;
    }

    reg_val = MDIO_GET_REG_BIT(mdio_dev, MDIO_STA_REG, MDIO_STATE_STA_B);
    if (reg_val) {
        dev_err(&bus->dev, " ERROR! MDIO Read failed!\n");
        return -EBUSY;
    }
// 最终读到phy 寄存器的值reg_val,然后返回这个值
    /* Step 6; get out data*/
    reg_val = (u16)MDIO_GET_REG_FIELD(mdio_dev, MDIO_RDATA_REG,
                      MDIO_RDATA_DATA_M, MDIO_RDATA_DATA_S);

    return reg_val;
}

总结一下hns_mdio.c 中主要通过mdiobus_register注册mdio bus,通过这个bus的read/write/reset 函数就可以操作phy register
原创粉丝点击