mdio-hisi-femac.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Hisilicon Fast Ethernet MDIO Bus Driver
  4. *
  5. * Copyright (c) 2016 HiSilicon Technologies Co., Ltd.
  6. */
  7. #include <linux/clk.h>
  8. #include <linux/iopoll.h>
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/of_address.h>
  12. #include <linux/of_mdio.h>
  13. #include <linux/platform_device.h>
  14. #define MDIO_RWCTRL 0x00
  15. #define MDIO_RO_DATA 0x04
  16. #define MDIO_WRITE BIT(13)
  17. #define MDIO_RW_FINISH BIT(15)
  18. #define BIT_PHY_ADDR_OFFSET 8
  19. #define BIT_WR_DATA_OFFSET 16
  20. struct hisi_femac_mdio_data {
  21. struct clk *clk;
  22. void __iomem *membase;
  23. };
  24. static int hisi_femac_mdio_wait_ready(struct hisi_femac_mdio_data *data)
  25. {
  26. u32 val;
  27. return readl_poll_timeout(data->membase + MDIO_RWCTRL,
  28. val, val & MDIO_RW_FINISH, 20, 10000);
  29. }
  30. static int hisi_femac_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
  31. {
  32. struct hisi_femac_mdio_data *data = bus->priv;
  33. int ret;
  34. ret = hisi_femac_mdio_wait_ready(data);
  35. if (ret)
  36. return ret;
  37. writel((mii_id << BIT_PHY_ADDR_OFFSET) | regnum,
  38. data->membase + MDIO_RWCTRL);
  39. ret = hisi_femac_mdio_wait_ready(data);
  40. if (ret)
  41. return ret;
  42. return readl(data->membase + MDIO_RO_DATA) & 0xFFFF;
  43. }
  44. static int hisi_femac_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
  45. u16 value)
  46. {
  47. struct hisi_femac_mdio_data *data = bus->priv;
  48. int ret;
  49. ret = hisi_femac_mdio_wait_ready(data);
  50. if (ret)
  51. return ret;
  52. writel(MDIO_WRITE | (value << BIT_WR_DATA_OFFSET) |
  53. (mii_id << BIT_PHY_ADDR_OFFSET) | regnum,
  54. data->membase + MDIO_RWCTRL);
  55. return hisi_femac_mdio_wait_ready(data);
  56. }
  57. static int hisi_femac_mdio_probe(struct platform_device *pdev)
  58. {
  59. struct device_node *np = pdev->dev.of_node;
  60. struct mii_bus *bus;
  61. struct hisi_femac_mdio_data *data;
  62. int ret;
  63. bus = mdiobus_alloc_size(sizeof(*data));
  64. if (!bus)
  65. return -ENOMEM;
  66. bus->name = "hisi_femac_mii_bus";
  67. bus->read = &hisi_femac_mdio_read;
  68. bus->write = &hisi_femac_mdio_write;
  69. snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
  70. bus->parent = &pdev->dev;
  71. data = bus->priv;
  72. data->membase = devm_platform_ioremap_resource(pdev, 0);
  73. if (IS_ERR(data->membase)) {
  74. ret = PTR_ERR(data->membase);
  75. goto err_out_free_mdiobus;
  76. }
  77. data->clk = devm_clk_get(&pdev->dev, NULL);
  78. if (IS_ERR(data->clk)) {
  79. ret = PTR_ERR(data->clk);
  80. goto err_out_free_mdiobus;
  81. }
  82. ret = clk_prepare_enable(data->clk);
  83. if (ret)
  84. goto err_out_free_mdiobus;
  85. ret = of_mdiobus_register(bus, np);
  86. if (ret)
  87. goto err_out_disable_clk;
  88. platform_set_drvdata(pdev, bus);
  89. return 0;
  90. err_out_disable_clk:
  91. clk_disable_unprepare(data->clk);
  92. err_out_free_mdiobus:
  93. mdiobus_free(bus);
  94. return ret;
  95. }
  96. static int hisi_femac_mdio_remove(struct platform_device *pdev)
  97. {
  98. struct mii_bus *bus = platform_get_drvdata(pdev);
  99. struct hisi_femac_mdio_data *data = bus->priv;
  100. mdiobus_unregister(bus);
  101. clk_disable_unprepare(data->clk);
  102. mdiobus_free(bus);
  103. return 0;
  104. }
  105. static const struct of_device_id hisi_femac_mdio_dt_ids[] = {
  106. { .compatible = "hisilicon,hisi-femac-mdio" },
  107. { }
  108. };
  109. MODULE_DEVICE_TABLE(of, hisi_femac_mdio_dt_ids);
  110. static struct platform_driver hisi_femac_mdio_driver = {
  111. .probe = hisi_femac_mdio_probe,
  112. .remove = hisi_femac_mdio_remove,
  113. .driver = {
  114. .name = "hisi-femac-mdio",
  115. .of_match_table = hisi_femac_mdio_dt_ids,
  116. },
  117. };
  118. module_platform_driver(hisi_femac_mdio_driver);
  119. MODULE_DESCRIPTION("Hisilicon Fast Ethernet MAC MDIO interface driver");
  120. MODULE_AUTHOR("Dongpo Li <lidongpo@hisilicon.com>");
  121. MODULE_LICENSE("GPL");