Demo参考

该验证使用eth1网口作为验证对象,验证过程中会修改PHY的寄存器,不要使用此网口通信。

以下以GE类型的PHY读写为例。

  1. 使用root用户登录Linux服务器,详细操作请参见使用PuTTY登录设备(网口方式)
  2. 使用WinSCP工具或其他工具,将下载软件包获取的源码包“Ascend310B-source.tar.gz”上传至Linux系统root用户属组目录下,例如“/opt”。详细操作请参见使用WinSCP传输文件
  3. 执行如下命令,进入源码包所在目录,例如“/opt”。

    cd /opt

  4. 执行如下命令,解压源码包“Ascend310B-source.tar.gz”。

    tar -xzvf Ascend310B-source.tar.gz

  5. 执行如下命令,进入“product”目录。

    cd Ascend310B-source/dtb/dts/hi1910b/hi1910BL/product

  6. 增加dts配置,以Atlas 200I DK A2 开发者套件设备上使用mdio1接口为例。

    1. 执行以下命令,编辑“hi1910B-pinctrl-M100-B51.dtsi”文件。

      vim hi1910B-pinctrl-M100-B51.dtsi

      在“hi1910B-pinctrl-M100-B51.dtsi”中增加mdio1接口的复用配置。
             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>;
                      };
              };
    2. 配置完成后,按“Esc”键,再执行:wq!保存修改,并按“Enter”键退出。

  7. 新增“hi1910B-customize-mdio.dtsi”文件。

    1. 执行如下命令,新增“hi1910B-customize-mdio.dtsi”文件。

      vim hi1910B-customize-mdio.dtsi

    2. 增加如下内容。
              mdio@a0190000 {
                      compatible = "hisilicon,hi1910b-mdio";
                      reg = <0x0 0xa0190000 0x0 0x1000>,
                            <0x0 0xa0120000 0x0 0x1000>;
                      #address-cells = <0x1>;
                     #size-cells = <0x0>;
                };
    3. 配置完成后,按“Esc”键,再执行:wq!保存修改,并按“Enter”键退出。

  8. 板级的dts文件中增加“hi1910B-customize-mdio.dtsi”文件引用。

    Atlas 200I DK A2 开发者套件的配置为例。

    1. 执行如下命令,进入dts文件所在目录。

      cd /opt/Ascend310B-source/dtb/dts/hi1910b/hi1910BL

    2. 执行如下命令,进入“hi1910B-asic-M100-B51.dts”编辑模式。

      vim hi1910B-asic-M100-B51.dts

    3. 在“hi1910B-asic-M100-B51.dts”中增加/include/ "product/hi1910B-customize-mdio.dtsi",如下图所示。

    4. 配置完成后,按“Esc”键,再执行:wq!保存修改,并按“Enter”键退出。

  9. 执行如下命令,回到“Ascend310B-source”目录下。

    cd /opt/Ascend310B-source

  10. 执行如下命令,编译DTB文件。

    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”目录下。

  11. 执行如下命令,进入“Ascend310B-source”目录。

    cd /opt/Ascend310B-source

  12. 执行如下命令,编译内核源码。

    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!

  13. 增加mdio的驱动代码。

    1. 执行如下命令,进入“Ascend310B-source/driver/drivers”目录。

      cd /opt/Ascend310B-source/driver/drivers

    2. 执行如下命令,创建并进入“usr”目录。

      mkdir usr

      cd usr

    3. 执行如下命令,创建“hns_mdio.c”文件。

      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");
      
    4. 执行如下命令,创建Makefile文件。

      vim Makefile

      添加如下代码,配置完成后,按“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
    5. 执行如下命令,创建module.mk文件。

      vim module.mk

      添加如下代码,配置完成后,按“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)
      

  14. 执行如下命令,编译驱动。

    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'
    • 编译后生成的“hns_mdio_daemo.ko”在当前目录下。
    • 下次编译前建议执行make clean清除上次编译生成的可执行文件及配置文件。

  15. 验证是否可以通过mdio访问PHY芯片。

    1. 升级10中dt.img,然后重启系统,具体请参见生效DTB文件
    2. 登录Atlas 200I DK A2 开发者套件系统环境。
    3. 使用“WinSCP”,将编译后的hns_mdio_daemo.ko文件上传至任意目录下,例如“/run”。详细操作请参见使用WinSCP传输文件
    4. 执行如下命令,进入“run”目录,再生效“hns_mdio_daemo.ko”文件。

      cd /run

      insmod hns_mdio_daemo.ko

    5. 执行如下命令,查看驱动模块是否加载成功。

      lsmod | grep hns_mdio

      出现以下回显表示加载成功。
      hns_mdio_daemo         16384  0
    6. 执行下dmesg,在结尾出现如下打印,表示可以通过mdio正常读写PHY的寄存器。