spl2sw_mdio.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Copyright Sunplus Technology Co., Ltd.
  3. * All rights reserved.
  4. */
  5. #include <linux/platform_device.h>
  6. #include <linux/netdevice.h>
  7. #include <linux/bitfield.h>
  8. #include <linux/of_mdio.h>
  9. #include "spl2sw_register.h"
  10. #include "spl2sw_define.h"
  11. #include "spl2sw_mdio.h"
  12. #define SPL2SW_MDIO_READ_CMD 0x02
  13. #define SPL2SW_MDIO_WRITE_CMD 0x01
  14. static int spl2sw_mdio_access(struct spl2sw_common *comm, u8 cmd, u8 addr, u8 regnum, u16 wdata)
  15. {
  16. u32 reg, reg2;
  17. u32 val;
  18. int ret;
  19. /* Note that addr (of phy) should match either ext_phy0_addr
  20. * or ext_phy1_addr, or mdio commands won't be sent out.
  21. */
  22. reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
  23. reg &= ~MAC_EXT_PHY0_ADDR;
  24. reg |= FIELD_PREP(MAC_EXT_PHY0_ADDR, addr);
  25. reg2 = FIELD_PREP(MAC_CPU_PHY_WT_DATA, wdata) | FIELD_PREP(MAC_CPU_PHY_CMD, cmd) |
  26. FIELD_PREP(MAC_CPU_PHY_REG_ADDR, regnum) | FIELD_PREP(MAC_CPU_PHY_ADDR, addr);
  27. /* Set ext_phy0_addr and then issue mdio command.
  28. * No interrupt is allowed in between.
  29. */
  30. spin_lock_irq(&comm->mdio_lock);
  31. writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
  32. writel(reg2, comm->l2sw_reg_base + L2SW_PHY_CNTL_REG0);
  33. spin_unlock_irq(&comm->mdio_lock);
  34. ret = read_poll_timeout(readl, val, val & cmd, 1, 1000, true,
  35. comm->l2sw_reg_base + L2SW_PHY_CNTL_REG1);
  36. /* Set ext_phy0_addr back to 31 to prevent
  37. * from sending mdio command to phy by
  38. * hardware auto-mdio function.
  39. */
  40. reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
  41. reg &= ~MAC_EXT_PHY0_ADDR;
  42. reg |= FIELD_PREP(MAC_EXT_PHY0_ADDR, 31);
  43. writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
  44. if (ret == 0)
  45. return val >> 16;
  46. else
  47. return ret;
  48. }
  49. static int spl2sw_mii_read(struct mii_bus *bus, int addr, int regnum)
  50. {
  51. struct spl2sw_common *comm = bus->priv;
  52. return spl2sw_mdio_access(comm, SPL2SW_MDIO_READ_CMD, addr, regnum, 0);
  53. }
  54. static int spl2sw_mii_write(struct mii_bus *bus, int addr, int regnum, u16 val)
  55. {
  56. struct spl2sw_common *comm = bus->priv;
  57. int ret;
  58. ret = spl2sw_mdio_access(comm, SPL2SW_MDIO_WRITE_CMD, addr, regnum, val);
  59. if (ret < 0)
  60. return ret;
  61. return 0;
  62. }
  63. u32 spl2sw_mdio_init(struct spl2sw_common *comm)
  64. {
  65. struct device_node *mdio_np;
  66. struct mii_bus *mii_bus;
  67. int ret;
  68. /* Get mdio child node. */
  69. mdio_np = of_get_child_by_name(comm->pdev->dev.of_node, "mdio");
  70. if (!mdio_np) {
  71. dev_err(&comm->pdev->dev, "No mdio child node found!\n");
  72. return -ENODEV;
  73. }
  74. /* Allocate and register mdio bus. */
  75. mii_bus = devm_mdiobus_alloc(&comm->pdev->dev);
  76. if (!mii_bus) {
  77. ret = -ENOMEM;
  78. goto out;
  79. }
  80. mii_bus->name = "sunplus_mii_bus";
  81. mii_bus->parent = &comm->pdev->dev;
  82. mii_bus->priv = comm;
  83. mii_bus->read = spl2sw_mii_read;
  84. mii_bus->write = spl2sw_mii_write;
  85. snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&comm->pdev->dev));
  86. ret = of_mdiobus_register(mii_bus, mdio_np);
  87. if (ret) {
  88. dev_err(&comm->pdev->dev, "Failed to register mdiobus!\n");
  89. goto out;
  90. }
  91. comm->mii_bus = mii_bus;
  92. out:
  93. of_node_put(mdio_np);
  94. return ret;
  95. }
  96. void spl2sw_mdio_remove(struct spl2sw_common *comm)
  97. {
  98. if (comm->mii_bus) {
  99. mdiobus_unregister(comm->mii_bus);
  100. comm->mii_bus = NULL;
  101. }
  102. }