该验证使用eth1网口作为验证对象,验证过程中会修改PHY的寄存器,不要使用此网口通信。
以下以GE类型的PHY读写为例。
cd /opt
tar -xzvf Ascend310B-source.tar.gz
cd Ascend310B-source/dtb/dts/hi1910b/hi1910BL/product
vim hi1910B-pinctrl-M100-B51.dtsi
pmx_io: pinmux@a0140000 { compatible = "pinctrl-single", "pinctrl-single1"; reg = <0x0 0xa0140000 0x0 0x9c>; #gpio-range-cells = <3>; pinctrl-single,register-width = <32>; pinctrl-single,function-mask = <7>; pinctrl-names = "default", "idle"; pinctrl-0 = <&mdc1_pmx_func &mdio1_pmx_func>; pinctrl-1 = <&mdc1_pmx_idle &mdc1_pmx_idle>; mdc1_pmx_func: mdc1_pmx_func { pinctrl-single,pins = <0x70 0x0>; }; mdc1_pmx_idle: mdc1_pmx_idle { pinctrl-single,pins = <0x70 0x0>; }; mdio1_pmx_func: mdio1_pmx_func { pinctrl-single,pins = <0x74 0x0>; }; mdio1_pmx_idle: mdio1_pmx_idle { pinctrl-single,pins = <0x74 0x0>; }; };
以Atlas 200I DK A2 开发者套件的配置为例。
cd /opt/Ascend310B-source
bash build.sh dtb
出现如下回显,且生成“dt.img”文件表示编译内核DTB文件成功。
generate /opt/Ascend310B-source/output/dt.img success! sign /opt/Ascend310B-source/output/dt.img success!
编译后的dt.img文件会自动存放于“Ascend310B-source/output”目录下。
cd /opt/Ascend310B-source
bash build.sh kernelSource
出现如下类似回显表示编译成功,“/linux-4.19”为生成内核源码路径。
/opt/Ascend310B-source/driver kernel_directory is [/opt/Ascend310B-source/driver/kernel/linux-4.19] generate kernel source success!
cd /opt/Ascend310B-source/driver/drivers
mkdir usr
cd usr
vim hns_mdio.c
添加如下代码,配置完成后,按“Esc”键,再执行:wq!保存修改,并按“Enter”键退出。
直接拷贝文档中代码到Linux系统中使用可能有格式不兼容问题,例如空格与Tab键使用错误,若出现类似报错,请先在Linux系统中调整代码格式正确。
#include <linux/errno.h> #include <linux/etherdevice.h> #include <linux/init.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/of_platform.h> #include <linux/phy.h> #include <linux/regmap.h> #include <linux/version.h> #include <linux/of_address.h> #include <linux/of_mdio.h> #include <linux/securec.h> #define MDIO_CDEV_MINORS 2 #define PHY_ADDR_FIXED 0 #define MDIO_CDEV_NAME "davinci_mdio_daemo" #define MDIO_DRV_NAME "HiAscend_MDIO_Daemo" #define MDIO_BUS_NAME "Hisilicon Ascend MII Bus Daemo" #define MDIO_TIMEOUT 1000000 /* cfg phy bit map */ #define MDIO_CMD_ST_S 12 #define MDIO_CMD_START_B 14 #define MDIO_CMD_PRTAD_S 5 #define MDIO_CMD_OP_S 10 #define MDIO_CMD_DEVAD_M 0x1f #define MDIO_CMD_DEVAD_S 0 #define MDIO_CMD_PRTAD_M 0x1f /* mdio reg */ #define MDIO_RDATA_REG 0xc #define MDIO_STA_REG 0x10 #define MDIO_COMMAND_REG 0x0 #define MDIO_ADDR_REG 0x4 #define MDIO_WDATA_REG 0x8 #define MDIO_WDATA_DATA_M 0xffff #define MDIO_WDATA_DATA_S 0 #define MDIO_RDATA_DATA_M 0xffff #define MDIO_RDATA_DATA_S 0 #define MDIO_ADDR_DATA_M 0xffff #define MDIO_ADDR_DATA_S 0 #define MDIO_STATE_STA_B 0 enum mdio_st_clause { MDIO_ST_CLAUSE_45 = 0, MDIO_ST_CLAUSE_22 }; enum { MEM_MDIO_IOBASE, MEM_SYSCTRL_IOBASE, MEM_IOBASE_MAX, }; enum mdio_c22_op_seq { MDIO_C22_WRITE = 1, MDIO_C22_READ = 2 }; struct peri_sc_mdio_reg { u16 mdio_clk_en; u16 mdio_clk_dis; u16 mdio_reset_req; u16 mdio_reset_dreq; u16 mdio_ctrl; u16 mdio_clk_st; u16 mdio_reset_st; }; enum mdio_c45_op_seq { MDIO_C45_WRITE_ADDR = 0, MDIO_C45_WRITE_DATA, MDIO_C45_READ_INCREMENT, MDIO_C45_READ }; struct hns_mdio_device { void *vbase; /* mdio reg base address */ struct regmap *subctrl_vbase; const struct peri_sc_mdio_reg *sc_reg; struct platform_device *pdev; struct mii_bus *bus; struct cdev mdio_cdev; struct class *mdio_cdev_cls; dev_t mdio_devno; }; static struct mii_bus *hns_mdiobus = NULL; static void mdio_write_reg(void *base, u32 reg, u32 value) { u8 __iomem *reg_addr = (u8 __iomem *)base; writel_relaxed(value, reg_addr + reg); } #define MDIO_WRITE_REG(a, reg, value) mdio_write_reg((a)->vbase, (reg), (value)) static u32 mdio_read_reg(void *base, u32 reg) { u8 __iomem *reg_addr = (u8 __iomem *)base; return readl_relaxed(reg_addr + reg); } #define mdio_set_field(origin, mask, shift, val) do { \ (origin) &= (~((mask) << (shift))); \ (origin) |= (((val) & (mask)) << (shift)); \ } while (0) #define mdio_get_field(origin, mask, shift) (((origin) >> (shift)) & (mask)) static void mdio_set_reg_field(void *base, u32 reg, u32 mask, u32 shift, u32 val) { u32 origin = mdio_read_reg(base, reg); mdio_set_field(origin, mask, shift, val); mdio_write_reg(base, reg, origin); } #define MDIO_SET_REG_FIELD(dev, reg, mask, shift, val) mdio_set_reg_field((dev)->vbase, (reg), (mask), (shift), (val)) static u32 mdio_get_reg_field(void *base, u32 reg, u32 mask, u32 shift) { u32 origin; origin = mdio_read_reg(base, reg); return mdio_get_field(origin, mask, shift); } #define MDIO_GET_REG_FIELD(dev, reg, mask, shift) mdio_get_reg_field((dev)->vbase, (reg), (mask), (shift)) #define MDIO_GET_REG_BIT(dev, reg, bit) mdio_get_reg_field((dev)->vbase, (reg), 0x1ull, (bit)) static int hns_mdio_wait_ready(struct mii_bus *bus) { struct hns_mdio_device *mdio_dev = bus->priv; u32 cmd_reg_value = 0; int i; /* waitting for MDIO_COMMAND_REG 's mdio_start==0 */ /* after that can do read or write */ for (i = 0; i < MDIO_TIMEOUT; i++) { cmd_reg_value = MDIO_GET_REG_BIT(mdio_dev, MDIO_COMMAND_REG, MDIO_CMD_START_B); if (!cmd_reg_value) { break; } } if ((i == MDIO_TIMEOUT) && cmd_reg_value) { printk(KERN_ERR "hns mdio wait timeout.\n"); return -ETIMEDOUT; } return 0; } static void hns_mdio_cmd_write(struct hns_mdio_device *mdio_dev, u8 is_c45, u8 op, u8 phy_id, u16 cmd) { u32 cmd_reg_value; u8 st = is_c45 ? MDIO_ST_CLAUSE_45 : MDIO_ST_CLAUSE_22; cmd_reg_value = st << MDIO_CMD_ST_S; cmd_reg_value |= op << MDIO_CMD_OP_S; cmd_reg_value |= (phy_id & MDIO_CMD_PRTAD_M) << MDIO_CMD_PRTAD_S; cmd_reg_value |= (cmd & MDIO_CMD_DEVAD_M) << MDIO_CMD_DEVAD_S; cmd_reg_value |= 1 << MDIO_CMD_START_B; MDIO_WRITE_REG(mdio_dev, MDIO_COMMAND_REG, cmd_reg_value); } static int hns_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 data) { int ret; struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; u8 devad = (((u32)regnum >> 16) & 0x1f); u8 is_c45 = !!((u32)regnum & MII_ADDR_C45); u16 reg = (u16)((u32)regnum & 0xffff); u8 op; u16 cmd_reg_cfg; /* wait for ready */ ret = hns_mdio_wait_ready(bus); if (ret != 0) { printk(KERN_ERR "MDIO bus is busy\n"); return ret; } if (!is_c45) { cmd_reg_cfg = reg; op = MDIO_C22_WRITE; } else { MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, MDIO_ADDR_DATA_S, reg); hns_mdio_cmd_write(mdio_dev, is_c45, MDIO_C45_WRITE_ADDR, phy_id, devad); /* check for read or write opt is finished */ ret = hns_mdio_wait_ready(bus); if (ret != 0) { printk(KERN_ERR "MDIO bus is busy\n"); return ret; } /* config the data needed writing */ cmd_reg_cfg = devad; op = MDIO_C45_WRITE_ADDR; } MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M, MDIO_WDATA_DATA_S, data); hns_mdio_cmd_write(mdio_dev, is_c45, op, phy_id, cmd_reg_cfg); return 0; } static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum) { int ret; u16 reg_val; u8 devad = (((u32)regnum >> 16) & 0x1f); u8 is_c45 = !!((u32)regnum & MII_ADDR_C45); u16 reg = (u16)((u32)regnum & 0xffff); struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; /* Step 1: wait for ready */ ret = hns_mdio_wait_ready(bus); if (ret != 0) { printk(KERN_ERR "MDIO bus is busy\n"); return ret; } 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 != 0) { printk(KERN_ERR "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 != 0) { printk(KERN_ERR "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 != 0) { printk(KERN_ERR "ERROR! MDIO Read failed!\n"); return -EBUSY; } /* 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_bus_name - get mdio bus name * @name: mdio bus name * @np: mdio device node pointer */ static void hns_mdio_bus_name(char *name, struct device_node *np) { const u32 *addr = NULL; u64 taddr = OF_BAD_ADDR; int ret; addr = of_get_address(np, 0, NULL, NULL); if (addr != NULL) { taddr = of_translate_address(np, addr); } ret = snprintf_s(name, MII_BUS_ID_SIZE, MII_BUS_ID_SIZE - 1, "%s@%llx", np->name, (unsigned long long)taddr); if (ret > 0) { printk(KERN_INFO "get bus name suceess\n"); } } static int mdio_open(struct inode *node, struct file *file) { struct hns_mdio_device *mdio_dev; mdio_dev = container_of(node->i_cdev, struct hns_mdio_device, mdio_cdev); file->private_data = (void *)mdio_dev; return 0; } static int mdio_release(struct inode *node, struct file *file) { file->private_data = NULL; return 0; } static long mdio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return 0; } static const struct file_operations mdio_ops = { .owner = THIS_MODULE, .open = mdio_open, .release = mdio_release, .unlocked_ioctl = mdio_ioctl, }; static int hns_mdio_cdev_create(struct hns_mdio_device *mdio_dev) { struct device *drv_dev = NULL; int ret; ret = alloc_chrdev_region(&mdio_dev->mdio_devno, 0, MDIO_CDEV_MINORS, MDIO_CDEV_NAME); if (ret != 0) { printk(KERN_ERR "alloc_chrdev_region failed, ret = %d\n", ret); goto _out; } mdio_dev->mdio_cdev_cls = class_create(THIS_MODULE, MDIO_CDEV_NAME); if (IS_ERR(mdio_dev->mdio_cdev_cls)) { printk(KERN_ERR "class_create faild, ret = %pK\n", mdio_dev->mdio_cdev_cls); goto _unregister_region; } drv_dev = device_create(mdio_dev->mdio_cdev_cls, NULL, mdio_dev->mdio_devno, NULL, MDIO_CDEV_NAME); if (IS_ERR(drv_dev)) { printk(KERN_ERR "failed to create device, ret = %ld\n", PTR_ERR(drv_dev)); goto _class_del; } cdev_init(&mdio_dev->mdio_cdev, &mdio_ops); mdio_dev->mdio_cdev.owner = THIS_MODULE; ret = cdev_add(&mdio_dev->mdio_cdev, mdio_dev->mdio_devno, MDIO_CDEV_MINORS); if (ret != 0) { printk(KERN_ERR "cdev_add failed, ret = %d\n", ret); goto _device_del; } printk(KERN_INFO "mdio cdev create finish, name is %s\n", MDIO_CDEV_NAME); return 0; _device_del: device_destroy(mdio_dev->mdio_cdev_cls, mdio_dev->mdio_devno); _class_del: class_destroy(mdio_dev->mdio_cdev_cls); _unregister_region: unregister_chrdev_region(mdio_dev->mdio_devno, MDIO_CDEV_MINORS); _out: return -EINVAL; } int hns_mdio_cdev_del(struct hns_mdio_device *mdio_dev) { cdev_del(&mdio_dev->mdio_cdev); device_destroy(mdio_dev->mdio_cdev_cls, mdio_dev->mdio_devno); class_destroy(mdio_dev->mdio_cdev_cls); unregister_chrdev_region(mdio_dev->mdio_devno, MDIO_CDEV_MINORS); mdio_dev->mdio_cdev_cls = NULL; printk(KERN_INFO "mdio cdev del finish\n"); return 0; } static const struct peri_sc_mdio_reg hi1911_peri_sc_mdio_reg = { .mdio_clk_en = 0x320, .mdio_clk_dis = 0x324, .mdio_reset_req = 0x418, .mdio_reset_dreq = 0x41C, .mdio_ctrl = 0, .mdio_clk_st = 0x5320, .mdio_reset_st = 0x5418, }; static const struct of_device_id hns_mdio_match[] = { { .compatible = "hisilicon,hi1910b-mdio", .data = &hi1911_peri_sc_mdio_reg }, { } }; MODULE_DEVICE_TABLE(of, hns_mdio_match); static int hns_mdiobus_register(struct device_node *np, struct mii_bus *new_bus, int *no_phy) { int ret; ret = of_mdiobus_register(new_bus, np); if (ret != 0) { printk(KERN_ERR "Cannot register as MDIO bus!\n"); return ret; } return ret; } static int hns_mdio_bus_info_init(struct platform_device *pdev, struct hns_mdio_device *mdio_dev, struct mii_bus *new_bus, struct device_node *np) { int ret; int no_phy = 0; struct resource *res = NULL; new_bus->name = MDIO_BUS_NAME; new_bus->read = hns_mdio_read; new_bus->write = hns_mdio_write; new_bus->priv = mdio_dev; hns_mdio_bus_name(new_bus->id, np); res = platform_get_resource(pdev, IORESOURCE_MEM, MEM_MDIO_IOBASE); mdio_dev->vbase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(mdio_dev->vbase)) { ret = PTR_ERR(mdio_dev->vbase); return ret; } res = platform_get_resource(pdev, IORESOURCE_MEM, MEM_SYSCTRL_IOBASE); mdio_dev->subctrl_vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (IS_ERR(mdio_dev->subctrl_vbase)) { ret = PTR_ERR(mdio_dev->subctrl_vbase); return ret; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) #else new_bus->irq = devm_kcalloc(&pdev->dev, PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); if (!new_bus->irq) { return -ENOMEM; } #endif new_bus->parent = &pdev->dev; platform_set_drvdata(pdev, new_bus); hns_mdiobus = new_bus; ret = hns_mdiobus_register(np, new_bus, &no_phy); if (ret != 0) { platform_set_drvdata(pdev, NULL); return ret; } ret = hns_mdio_cdev_create(mdio_dev); if (ret != 0) { printk(KERN_ERR "hns_mdio_cdev_create failed\n"); if (!no_phy) { mdiobus_unregister(new_bus); } platform_set_drvdata(pdev, NULL); } return 0; } static void hhns_mdio_test(struct mii_bus *bus) { u16 phy_id_1; u16 phy_id_2; u16 value; int ret; printk(KERN_INFO "Now let's test the mdio function."); ret = hns_mdio_read(bus, 0x1, 0x2); if (ret < 0) { printk(KERN_ERR "Function hns_mdio_read phy addr 0x1, reg 0x2 failed."); return; } phy_id_1 = (u16)ret; ret = hns_mdio_read(bus, 0x1, 0x3); if (ret < 0) { printk(KERN_ERR "Function hns_mdio_read phy addr 0x1, reg 0x3 failed."); return; } phy_id_2 = (u16)ret; printk(KERN_INFO "Now get phy addr 0x1, reg 0x2 value [0x%x].", phy_id_1); printk(KERN_INFO "Now get phy addr 0x1, reg 0x3 value [0x%x].", phy_id_2); ret = hns_mdio_read(bus, 0x1, 0x0); if (ret < 0) { printk(KERN_ERR "Function hns_mdio_read phy addr 0x1, reg 0x0 failed."); return; } value = (u16)ret; printk(KERN_INFO "Now get phy addr 0x1, reg 0x0 value [0x%x].", value); value &= 0xFEFF; ret = hns_mdio_write(bus, 0x1, 0x0, value); if (ret != 0) { printk(KERN_ERR "Function hns_mdio_write phy addr 0x1, reg 0x0 failed."); return; } printk(KERN_INFO "Now set phy addr 0x1, reg 0x0 value [0x%x].", value); ret = hns_mdio_read(bus, 0x1, 0x0); if (ret < 0) { printk(KERN_ERR "Function hns_mdio_read phy addr 0x1, reg 0x0 failed."); return; } value = (u16)ret; printk(KERN_INFO "Now get phy addr 0x1, reg 0x0 value [0x%x].", value); } /* * * hns_mdio_probe - probe mdio device * @pdev: mdio platform device * * Return 0 on success, negative on failure */ static int hns_mdio_probe(struct platform_device *pdev) { struct device_node *np = NULL; struct hns_mdio_device *mdio_dev = NULL; struct mii_bus *new_bus = NULL; const struct of_device_id *of_id = NULL; int ret; if (pdev == NULL) { printk(KERN_ERR "pdev is NULL!\n"); return -ENODEV; } np = pdev->dev.of_node; mdio_dev = (struct hns_mdio_device *)devm_kzalloc(&pdev->dev, sizeof(struct hns_mdio_device), GFP_KERNEL); if (mdio_dev == NULL) { return -ENOMEM; } new_bus = devm_mdiobus_alloc(&pdev->dev); if (new_bus == NULL) { printk(KERN_ERR "mdiobus_alloc fail!\n"); return -ENOMEM; } of_id = of_match_device(hns_mdio_match, &pdev->dev); if (of_id == NULL) { return -ENODEV; } mdio_dev->sc_reg = of_id->data; ret = hns_mdio_bus_info_init(pdev, mdio_dev, new_bus, np); if (ret != 0) { printk(KERN_ERR "hns_mdio_bus_info_init failed.(ret=%d)", ret); return ret; } hhns_mdio_test(new_bus); return 0; } static int hns_mdio_remove(struct platform_device *pdev) { struct hns_mdio_device *mdio_dev = NULL; struct mii_bus *bus = NULL; bus = platform_get_drvdata(pdev); if (bus == NULL) { printk(KERN_ERR "bus is NULL"); return -EINVAL; } mdio_dev = (struct hns_mdio_device *)bus->priv; if (mdio_dev == NULL) { printk(KERN_ERR "mdio_dev is NULL"); return -EINVAL; } mdiobus_unregister(bus); platform_set_drvdata(pdev, NULL); hns_mdio_cdev_del(mdio_dev); printk(KERN_INFO "mdio remove finish\n"); return 0; } static struct platform_driver hns_mdio_driver = { .remove = hns_mdio_remove, .probe = hns_mdio_probe, .driver = { .of_match_table = hns_mdio_match, .name = MDIO_DRV_NAME, }, }; module_platform_driver(hns_mdio_driver); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" MDIO_DRV_NAME); MODULE_DESCRIPTION("Hisilicon HNS MDIO Daemo driver");
添加如下代码,配置完成后,按“Esc”键,再执行:wq!保存修改,并按“Enter”键退出。
直接拷贝文档中代码到Linux系统中使用可能有格式不兼容问题,例如空格与Tab键使用错误,若出现类似报错,请先在Linux系统中调整代码格式正确。
ccflags-y += -Wall -Werror -Wtrampolines $(WDATE_TIME) -Wfloat-equal -Wvla -Wundef -funsigned-char -Wformat=2 -Wstack-usage=2048 -Wcast-align ccflags-y += -Wextra -Wno-unused-parameter -Wno-sign-compare -Wno-missing-field-initializers obj-m += hns_mdio_daemo.o hns_mdio_daemo-objs := hns_mdio.o .PHONY : clean clean : rm -fr Module.symvers ; rm -fr Module.markers ; rm -fr modules.order rm -fr *.mod.c *.mod *.o .*.cmd *.ko *~ rm -fr .tmp_versions
添加如下代码,配置完成后,按“Esc”键,再执行:wq!保存修改,并按“Enter”键退出。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hns_mdio_daemo LOCAL_KO_SRC_FOLDER := $(LOCAL_PATH) LOCAL_INSTALLED_KO_FILES := hns_mdio_daemo.ko include $(BUILD_DEVICE_KO)
make -C /opt/Ascend310B-source/driver/kernel/linux-4.19 ARCH=arm64 M=`pwd` modules CROSS_COMPILE=aarch64-target-linux-gnu-
类似回显信息如下。
make: Entering directory '/opt/Ascend310B-source/driver/kernel/linux-4.19' CC [M] /opt/Ascend310B-source/driver/drivers/usr/hns_mdio.o LD [M] /opt/Ascend310B-source/driver/drivers/usr/hns_mdio_daemo.o WARNING: Symbol version dump "Mr'mrmodule.symvers" is missing. Modules may not have dependencies or modversions. MODPOST /opt/Ascend310B-source/driver/drivers/usr/Module.symvers WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. CC [M] /opt/Ascend310B-source/driver/drivers/usr/hns_mdio_daemo.mod.o LD [M] /opt/Ascend310B-source/driver/drivers/usr/hns_mdio_daemo.ko make: Leaving directory '/opt/Ascend310B-source/driver/kernel/linux-4.19'
cd /run
insmod hns_mdio_daemo.ko
hns_mdio_daemo 16384 0