mdio-ipq4019.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Qualcomm IPQ4019 MDIO driver
  4. *
  5. * Copyright (c) 2020 Sartura Ltd.
  6. *
  7. * Author: Luka Kovacic <luka.kovacic@sartura.hr>
  8. * Author: Robert Marko <robert.marko@sartura.hr>
  9. *
  10. * Based on Linux driver
  11. */
  12. #include <asm/io.h>
  13. #include <common.h>
  14. #include <dm.h>
  15. #include <errno.h>
  16. #include <linux/bitops.h>
  17. #include <linux/iopoll.h>
  18. #include <miiphy.h>
  19. #include <phy.h>
  20. #define MDIO_MODE_REG 0x40
  21. #define MDIO_ADDR_REG 0x44
  22. #define MDIO_DATA_WRITE_REG 0x48
  23. #define MDIO_DATA_READ_REG 0x4c
  24. #define MDIO_CMD_REG 0x50
  25. #define MDIO_CMD_ACCESS_BUSY BIT(16)
  26. #define MDIO_CMD_ACCESS_START BIT(8)
  27. #define MDIO_CMD_ACCESS_CODE_READ 0
  28. #define MDIO_CMD_ACCESS_CODE_WRITE 1
  29. /* 0 = Clause 22, 1 = Clause 45 */
  30. #define MDIO_MODE_BIT BIT(8)
  31. #define IPQ4019_MDIO_TIMEOUT 10000
  32. #define IPQ4019_MDIO_SLEEP 10
  33. struct ipq4019_mdio_priv {
  34. phys_addr_t mdio_base;
  35. };
  36. static int ipq4019_mdio_wait_busy(struct ipq4019_mdio_priv *priv)
  37. {
  38. unsigned int busy;
  39. return readl_poll_sleep_timeout(priv->mdio_base + MDIO_CMD_REG, busy,
  40. (busy & MDIO_CMD_ACCESS_BUSY) == 0, IPQ4019_MDIO_SLEEP,
  41. IPQ4019_MDIO_TIMEOUT);
  42. }
  43. int ipq4019_mdio_read(struct udevice *dev, int addr, int devad, int reg)
  44. {
  45. struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
  46. unsigned int cmd;
  47. if (ipq4019_mdio_wait_busy(priv))
  48. return -ETIMEDOUT;
  49. /* Issue the phy address and reg */
  50. writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG);
  51. cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;
  52. /* Issue read command */
  53. writel(cmd, priv->mdio_base + MDIO_CMD_REG);
  54. /* Wait read complete */
  55. if (ipq4019_mdio_wait_busy(priv))
  56. return -ETIMEDOUT;
  57. /* Read and return data */
  58. return readl(priv->mdio_base + MDIO_DATA_READ_REG);
  59. }
  60. int ipq4019_mdio_write(struct udevice *dev, int addr, int devad,
  61. int reg, u16 val)
  62. {
  63. struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
  64. unsigned int cmd;
  65. if (ipq4019_mdio_wait_busy(priv))
  66. return -ETIMEDOUT;
  67. /* Issue the phy addreass and reg */
  68. writel((addr << 8) | reg, priv->mdio_base + MDIO_ADDR_REG);
  69. /* Issue write data */
  70. writel(val, priv->mdio_base + MDIO_DATA_WRITE_REG);
  71. cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;
  72. /* Issue write command */
  73. writel(cmd, priv->mdio_base + MDIO_CMD_REG);
  74. /* Wait for write complete */
  75. if (ipq4019_mdio_wait_busy(priv))
  76. return -ETIMEDOUT;
  77. return 0;
  78. }
  79. static const struct mdio_ops ipq4019_mdio_ops = {
  80. .read = ipq4019_mdio_read,
  81. .write = ipq4019_mdio_write,
  82. };
  83. static int ipq4019_mdio_bind(struct udevice *dev)
  84. {
  85. if (ofnode_valid(dev->node))
  86. device_set_name(dev, ofnode_get_name(dev->node));
  87. return 0;
  88. }
  89. static int ipq4019_mdio_probe(struct udevice *dev)
  90. {
  91. struct ipq4019_mdio_priv *priv = dev_get_priv(dev);
  92. unsigned int data;
  93. priv->mdio_base = dev_read_addr(dev);
  94. if (priv->mdio_base == FDT_ADDR_T_NONE)
  95. return -EINVAL;
  96. /* Enter Clause 22 mode */
  97. data = readl(priv->mdio_base + MDIO_MODE_REG);
  98. data &= ~MDIO_MODE_BIT;
  99. writel(data, priv->mdio_base + MDIO_MODE_REG);
  100. return 0;
  101. }
  102. static const struct udevice_id ipq4019_mdio_ids[] = {
  103. { .compatible = "qcom,ipq4019-mdio", },
  104. { }
  105. };
  106. U_BOOT_DRIVER(ipq4019_mdio) = {
  107. .name = "ipq4019_mdio",
  108. .id = UCLASS_MDIO,
  109. .of_match = ipq4019_mdio_ids,
  110. .bind = ipq4019_mdio_bind,
  111. .probe = ipq4019_mdio_probe,
  112. .ops = &ipq4019_mdio_ops,
  113. .priv_auto_alloc_size = sizeof(struct ipq4019_mdio_priv),
  114. };