美文网首页
MII 接口解析(三)GPIO 模拟 MDIO 接口使用代码

MII 接口解析(三)GPIO 模拟 MDIO 接口使用代码

作者: Nothing_655f | 来源:发表于2020-09-23 18:29 被阅读0次

    GPIO 模拟 MDIO 接口使用代码
    如果要通过GPIO来模拟MDIO通信,那么我们就是通过模拟其帧格式的时序来通信,

    前面介绍过一遍其帧格式了,MDIO上数据帧的格式如下:

    mdio frame

    内核中提供了一个mdio-bitbang.c,里面实现了一套软件实现的MDIO/MDC接口时序可供参考。

    数据结构

    struct mdiobb_ops {
        struct module *owner;
        /* Set the Management Data Clock high if level is one,
         * low if level is zero.
         */
        void (*set_mdc)(struct mdiobb_ctrl *ctrl, int level);
        /* Configure the Management Data I/O pin as an input if
         * "output" is zero, or an output if "output" is one.
         */
        void (*set_mdio_dir)(struct mdiobb_ctrl *ctrl, int output);
        /* Set the Management Data I/O pin high if value is one,
         * low if "value" is zero.  This may only be called
         * when the MDIO pin is configured as an output.
         */
        void (*set_mdio_data)(struct mdiobb_ctrl *ctrl, int value);
        /* Retrieve the state Management Data I/O pin. */
        int (*get_mdio_data)(struct mdiobb_ctrl *ctrl);
    };
    struct mdiobb_ctrl {
        const struct mdiobb_ops *ops;
    };
    

    set_mdc,发送MDC 信号的函数,由具体使用情况来决定实际的mdc 信号是如何发送
    set_mdio_dir,设置MDIO方向的函数,由具体使用情况来决定实际的mdc 信号是如何发送
    set_mdio_data,发送MDIO数据到MMD,由具体使用情况来决定实际的mdc 信号是如何发送
    get_mdio_data,从MMD接收MDIO数据,由具体使用情况来决定实际的mdc 信号是如何发送
    使用方法
    通过调用 alloc_mdio_bitbang 来获得一个mii_bus, 并且注册read/wirte 方法为 mdiobb_read/mdiobb_write,并且将mdiobb_ctrl 作为mii bus的私有成员来操作器后续的ops 函数

    struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
    {
        struct mii_bus *bus;
        bus = mdiobus_alloc();
        if (!bus)
            return NULL;
        __module_get(ctrl->ops->owner);
        bus->read = mdiobb_read;
        bus->write = mdiobb_write;
        bus->priv = ctrl;
        return bus;
    }
    

    以wirte 为例,bitbang中帮我们封装好了对应的时序,我们需要做的就是定义好前面注册的 ops mdc mdio操作

    static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
    {
        struct mdiobb_ctrl *ctrl = bus->priv;
        if (reg & MII_ADDR_C45) {
            reg = mdiobb_cmd_addr(ctrl, phy, reg);
            mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg);
        } else
            mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg);
        /* send the turnaround (10) */
        mdiobb_send_bit(ctrl, 1);
        mdiobb_send_bit(ctrl, 0);
        mdiobb_send_num(ctrl, val, 16);
        ctrl->ops->set_mdio_dir(ctrl, 0);
        mdiobb_get_bit(ctrl);
        return 0;
    }
    

    内核中也有一个GPIOI-MDIO的例子,可以参考其来移植到自己的驱动中

    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/interrupt.h>
    #include <linux/platform_device.h>
    #include <linux/platform_data/mdio-gpio.h>
    #include <linux/mdio-bitbang.h>
    #include <linux/mdio-gpio.h>
    #include <linux/gpio/consumer.h>
    #include <linux/of_mdio.h>
    struct mdio_gpio_info {
        struct mdiobb_ctrl ctrl;
        struct gpio_desc *mdc, *mdio, *mdo;
    };
    static int mdio_gpio_get_data(struct device *dev,
                      struct mdio_gpio_info *bitbang)
    {
        bitbang->mdc = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDC,
                            GPIOD_OUT_LOW);
        if (IS_ERR(bitbang->mdc))
            return PTR_ERR(bitbang->mdc);
        bitbang->mdio = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDIO,
                             GPIOD_IN);
        if (IS_ERR(bitbang->mdio))
            return PTR_ERR(bitbang->mdio);
        bitbang->mdo = devm_gpiod_get_index_optional(dev, NULL, MDIO_GPIO_MDO,
                                 GPIOD_OUT_LOW);
        return PTR_ERR_OR_ZERO(bitbang->mdo);
    }
    static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
    {
        struct mdio_gpio_info *bitbang =
            container_of(ctrl, struct mdio_gpio_info, ctrl);
        if (bitbang->mdo) {
            /* Separate output pin. Always set its value to high
             * when changing direction. If direction is input,
             * assume the pin serves as pull-up. If direction is
             * output, the default value is high.
             */
            gpiod_set_value_cansleep(bitbang->mdo, 1);
            return;
        }
        if (dir)
            gpiod_direction_output(bitbang->mdio, 1);
        else
            gpiod_direction_input(bitbang->mdio);
    }
    static int mdio_get(struct mdiobb_ctrl *ctrl)
    {
        struct mdio_gpio_info *bitbang =
            container_of(ctrl, struct mdio_gpio_info, ctrl);
        return gpiod_get_value_cansleep(bitbang->mdio);
    }
    static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
    {
        struct mdio_gpio_info *bitbang =
            container_of(ctrl, struct mdio_gpio_info, ctrl);
        if (bitbang->mdo)
            gpiod_set_value_cansleep(bitbang->mdo, what);
        else
            gpiod_set_value_cansleep(bitbang->mdio, what);
    }
    static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
    {
        struct mdio_gpio_info *bitbang =
            container_of(ctrl, struct mdio_gpio_info, ctrl);
        gpiod_set_value_cansleep(bitbang->mdc, what);
    }
    static const struct mdiobb_ops mdio_gpio_ops = {
        .owner = THIS_MODULE,
        .set_mdc = mdc_set,
        .set_mdio_dir = mdio_dir,
        .set_mdio_data = mdio_set,
        .get_mdio_data = mdio_get,
    };
    static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
                          struct mdio_gpio_info *bitbang,
                          int bus_id)
    {
        struct mdio_gpio_platform_data *pdata = dev_get_platdata(dev);
        struct mii_bus *new_bus;
        bitbang->ctrl.ops = &mdio_gpio_ops;
        new_bus = alloc_mdio_bitbang(&bitbang->ctrl);
        if (!new_bus)
            return NULL;
        new_bus->name = "GPIO Bitbanged MDIO";
        new_bus->parent = dev;
        if (bus_id != -1)
            snprintf(new_bus->id, MII_BUS_ID_SIZE, "gpio-%x", bus_id);
        else
            strncpy(new_bus->id, "gpio", MII_BUS_ID_SIZE);
        if (pdata) {
            new_bus->phy_mask = pdata->phy_mask;
            new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask;
        }
        dev_set_drvdata(dev, new_bus);
        return new_bus;
    }
    static void mdio_gpio_bus_deinit(struct device *dev)
    {
        struct mii_bus *bus = dev_get_drvdata(dev);
        free_mdio_bitbang(bus);
    }
    static void mdio_gpio_bus_destroy(struct device *dev)
    {
        struct mii_bus *bus = dev_get_drvdata(dev);
        mdiobus_unregister(bus);
        mdio_gpio_bus_deinit(dev);
    }
    static int mdio_gpio_probe(struct platform_device *pdev)
    {
        struct mdio_gpio_info *bitbang;
        struct mii_bus *new_bus;
        int ret, bus_id;
        bitbang = devm_kzalloc(&pdev->dev, sizeof(*bitbang), GFP_KERNEL);
        if (!bitbang)
            return -ENOMEM;
        ret = mdio_gpio_get_data(&pdev->dev, bitbang);
        if (ret)
            return ret;
        if (pdev->dev.of_node) {
            bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio");
            if (bus_id < 0) {
                dev_warn(&pdev->dev, "failed to get alias id\n");
                bus_id = 0;
            }
        } else {
            bus_id = pdev->id;
        }
        new_bus = mdio_gpio_bus_init(&pdev->dev, bitbang, bus_id);
        if (!new_bus)
            return -ENODEV;
        ret = of_mdiobus_register(new_bus, pdev->dev.of_node);
        if (ret)
            mdio_gpio_bus_deinit(&pdev->dev);
        return ret;
    }
    static int mdio_gpio_remove(struct platform_device *pdev)
    {
        mdio_gpio_bus_destroy(&pdev->dev);
        return 0;
    }
    static const struct of_device_id mdio_gpio_of_match[] = {
        { .compatible = "virtual,mdio-gpio", },
        { /* sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, mdio_gpio_of_match);
    static struct platform_driver mdio_gpio_driver = {
        .probe = mdio_gpio_probe,
        .remove = mdio_gpio_remove,
        .driver     = {
            .name   = "mdio-gpio",
            .of_match_table = mdio_gpio_of_match,
        },
    };
    module_platform_driver(mdio_gpio_driver);
    MODULE_ALIAS("platform:mdio-gpio");
    MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas");
    MODULE_LICENSE("GPL v2");
    MODULE_DESCRIPTION("Generic driver for MDIO bus emulation using GPIO");
    

    DTS 配置

    参考 Documentation/devicetree/bindings/net/mdio-gpio.txt 配置即可

    MII 接口解析(一)MII 等类型接口简介
    MII 接口解析(二)MII 数据类型和寄存器
    MII 接口解析(三)GPIO 模拟 MDIO 接口使用代码

    相关文章

      网友评论

          本文标题:MII 接口解析(三)GPIO 模拟 MDIO 接口使用代码

          本文链接:https://www.haomeiwen.com/subject/vluwyktx.html