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,
®_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
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,
®_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
阅读全文
0 0
- hns_mdio通过注册mdio bus对phy寄存器操作
- PHY管理接口(MDIO)
- PHY管理接口(MDIO)
- PHY管理接口MDIO时序
- PHY的MDIO/MDC简介
- zynq 7000 通过 ULPI 访问 usb phy 寄存器
- [转]PHY管理接口(MDIO)
- PHY管理接口(MDIO)[z]
- 通过ds寄存器操作内存
- linux网卡phy-mii驱动mdio调试zz150120a
- 以太网PHY 芯片之 MII/MDIO接口详解
- 以太网PHY 芯片之 MII/MDIO接口详解
- 以太网PHY 芯片之 MII/MDIO接口详解
- 以太网PHY 芯片之 MII/MDIO接口详解
- phy device的注册
- 以太网的phy寄存器分析
- 以太网的phy寄存器分析
- LED灯驱动编写----对寄存器操作
- 数据管理
- 自动化测试工具TW报错及解决方法
- 响铃:整合通信与视频,网易云能成为丁磊的下一个“爆款”吗?
- Vue.js基本指令
- 捣鼓PlantUML(三、时序图)
- hns_mdio通过注册mdio bus对phy寄存器操作
- sort -u和uniq区别
- WPF--火车后台管理系统分析(一)
- 最直白理解NFC开发的三种工作模式
- 使用spring中的JdbcTemplate调用oracle中的有出入参数的存储过程
- Python(2) import .py文件
- error C1010: 在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include "stdafx.h”
- 经典冒泡排序法,传引用参数的那种,面试通用
- 【栈 && 矩阵的知识】UVA 442 Matrix Chain Multiplication