mdio-ipq4019.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
  2. /* Copyright (c) 2015, The Linux Foundation. All rights reserved. */
  3. /* Copyright (c) 2020 Sartura Ltd. */
  4. #include <linux/delay.h>
  5. #include <linux/io.h>
  6. #include <linux/iopoll.h>
  7. #include <linux/kernel.h>
  8. #include <linux/module.h>
  9. #include <linux/of_address.h>
  10. #include <linux/of_mdio.h>
  11. #include <linux/phy.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/clk.h>
  14. #define MDIO_MODE_REG 0x40
  15. #define MDIO_ADDR_REG 0x44
  16. #define MDIO_DATA_WRITE_REG 0x48
  17. #define MDIO_DATA_READ_REG 0x4c
  18. #define MDIO_CMD_REG 0x50
  19. #define MDIO_CMD_ACCESS_BUSY BIT(16)
  20. #define MDIO_CMD_ACCESS_START BIT(8)
  21. #define MDIO_CMD_ACCESS_CODE_READ 0
  22. #define MDIO_CMD_ACCESS_CODE_WRITE 1
  23. #define MDIO_CMD_ACCESS_CODE_C45_ADDR 0
  24. #define MDIO_CMD_ACCESS_CODE_C45_WRITE 1
  25. #define MDIO_CMD_ACCESS_CODE_C45_READ 2
  26. /* 0 = Clause 22, 1 = Clause 45 */
  27. #define MDIO_MODE_C45 BIT(8)
  28. #define IPQ4019_MDIO_TIMEOUT 10000
  29. #define IPQ4019_MDIO_SLEEP 10
  30. /* MDIO clock source frequency is fixed to 100M */
  31. #define IPQ_MDIO_CLK_RATE 100000000
  32. #define IPQ_PHY_SET_DELAY_US 100000
  33. struct ipq4019_mdio_data {
  34. void __iomem *membase;
  35. void __iomem *eth_ldo_rdy;
  36. struct clk *mdio_clk;
  37. };
  38. static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
  39. {
  40. struct ipq4019_mdio_data *priv = bus->priv;
  41. unsigned int busy;
  42. return readl_poll_timeout(priv->membase + MDIO_CMD_REG, busy,
  43. (busy & MDIO_CMD_ACCESS_BUSY) == 0,
  44. IPQ4019_MDIO_SLEEP, IPQ4019_MDIO_TIMEOUT);
  45. }
  46. static int ipq4019_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
  47. {
  48. struct ipq4019_mdio_data *priv = bus->priv;
  49. unsigned int data;
  50. unsigned int cmd;
  51. if (ipq4019_mdio_wait_busy(bus))
  52. return -ETIMEDOUT;
  53. /* Clause 45 support */
  54. if (regnum & MII_ADDR_C45) {
  55. unsigned int mmd = (regnum >> 16) & 0x1F;
  56. unsigned int reg = regnum & 0xFFFF;
  57. /* Enter Clause 45 mode */
  58. data = readl(priv->membase + MDIO_MODE_REG);
  59. data |= MDIO_MODE_C45;
  60. writel(data, priv->membase + MDIO_MODE_REG);
  61. /* issue the phy address and mmd */
  62. writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG);
  63. /* issue reg */
  64. writel(reg, priv->membase + MDIO_DATA_WRITE_REG);
  65. cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR;
  66. } else {
  67. /* Enter Clause 22 mode */
  68. data = readl(priv->membase + MDIO_MODE_REG);
  69. data &= ~MDIO_MODE_C45;
  70. writel(data, priv->membase + MDIO_MODE_REG);
  71. /* issue the phy address and reg */
  72. writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
  73. cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_READ;
  74. }
  75. /* issue read command */
  76. writel(cmd, priv->membase + MDIO_CMD_REG);
  77. /* Wait read complete */
  78. if (ipq4019_mdio_wait_busy(bus))
  79. return -ETIMEDOUT;
  80. if (regnum & MII_ADDR_C45) {
  81. cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_READ;
  82. writel(cmd, priv->membase + MDIO_CMD_REG);
  83. if (ipq4019_mdio_wait_busy(bus))
  84. return -ETIMEDOUT;
  85. }
  86. /* Read and return data */
  87. return readl(priv->membase + MDIO_DATA_READ_REG);
  88. }
  89. static int ipq4019_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
  90. u16 value)
  91. {
  92. struct ipq4019_mdio_data *priv = bus->priv;
  93. unsigned int data;
  94. unsigned int cmd;
  95. if (ipq4019_mdio_wait_busy(bus))
  96. return -ETIMEDOUT;
  97. /* Clause 45 support */
  98. if (regnum & MII_ADDR_C45) {
  99. unsigned int mmd = (regnum >> 16) & 0x1F;
  100. unsigned int reg = regnum & 0xFFFF;
  101. /* Enter Clause 45 mode */
  102. data = readl(priv->membase + MDIO_MODE_REG);
  103. data |= MDIO_MODE_C45;
  104. writel(data, priv->membase + MDIO_MODE_REG);
  105. /* issue the phy address and mmd */
  106. writel((mii_id << 8) | mmd, priv->membase + MDIO_ADDR_REG);
  107. /* issue reg */
  108. writel(reg, priv->membase + MDIO_DATA_WRITE_REG);
  109. cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_ADDR;
  110. writel(cmd, priv->membase + MDIO_CMD_REG);
  111. if (ipq4019_mdio_wait_busy(bus))
  112. return -ETIMEDOUT;
  113. } else {
  114. /* Enter Clause 22 mode */
  115. data = readl(priv->membase + MDIO_MODE_REG);
  116. data &= ~MDIO_MODE_C45;
  117. writel(data, priv->membase + MDIO_MODE_REG);
  118. /* issue the phy address and reg */
  119. writel((mii_id << 8) | regnum, priv->membase + MDIO_ADDR_REG);
  120. }
  121. /* issue write data */
  122. writel(value, priv->membase + MDIO_DATA_WRITE_REG);
  123. /* issue write command */
  124. if (regnum & MII_ADDR_C45)
  125. cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_C45_WRITE;
  126. else
  127. cmd = MDIO_CMD_ACCESS_START | MDIO_CMD_ACCESS_CODE_WRITE;
  128. writel(cmd, priv->membase + MDIO_CMD_REG);
  129. /* Wait write complete */
  130. if (ipq4019_mdio_wait_busy(bus))
  131. return -ETIMEDOUT;
  132. return 0;
  133. }
  134. static int ipq_mdio_reset(struct mii_bus *bus)
  135. {
  136. struct ipq4019_mdio_data *priv = bus->priv;
  137. u32 val;
  138. int ret;
  139. /* To indicate CMN_PLL that ethernet_ldo has been ready if platform resource 1
  140. * is specified in the device tree.
  141. */
  142. if (priv->eth_ldo_rdy) {
  143. val = readl(priv->eth_ldo_rdy);
  144. val |= BIT(0);
  145. writel(val, priv->eth_ldo_rdy);
  146. fsleep(IPQ_PHY_SET_DELAY_US);
  147. }
  148. /* Configure MDIO clock source frequency if clock is specified in the device tree */
  149. ret = clk_set_rate(priv->mdio_clk, IPQ_MDIO_CLK_RATE);
  150. if (ret)
  151. return ret;
  152. ret = clk_prepare_enable(priv->mdio_clk);
  153. if (ret == 0)
  154. mdelay(10);
  155. return ret;
  156. }
  157. static int ipq4019_mdio_probe(struct platform_device *pdev)
  158. {
  159. struct ipq4019_mdio_data *priv;
  160. struct mii_bus *bus;
  161. struct resource *res;
  162. int ret;
  163. bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv));
  164. if (!bus)
  165. return -ENOMEM;
  166. priv = bus->priv;
  167. priv->membase = devm_platform_ioremap_resource(pdev, 0);
  168. if (IS_ERR(priv->membase))
  169. return PTR_ERR(priv->membase);
  170. priv->mdio_clk = devm_clk_get_optional(&pdev->dev, "gcc_mdio_ahb_clk");
  171. if (IS_ERR(priv->mdio_clk))
  172. return PTR_ERR(priv->mdio_clk);
  173. /* The platform resource is provided on the chipset IPQ5018 */
  174. /* This resource is optional */
  175. res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  176. if (res)
  177. priv->eth_ldo_rdy = devm_ioremap_resource(&pdev->dev, res);
  178. bus->name = "ipq4019_mdio";
  179. bus->read = ipq4019_mdio_read;
  180. bus->write = ipq4019_mdio_write;
  181. bus->reset = ipq_mdio_reset;
  182. bus->parent = &pdev->dev;
  183. snprintf(bus->id, MII_BUS_ID_SIZE, "%s%d", pdev->name, pdev->id);
  184. ret = of_mdiobus_register(bus, pdev->dev.of_node);
  185. if (ret) {
  186. dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
  187. return ret;
  188. }
  189. platform_set_drvdata(pdev, bus);
  190. return 0;
  191. }
  192. static int ipq4019_mdio_remove(struct platform_device *pdev)
  193. {
  194. struct mii_bus *bus = platform_get_drvdata(pdev);
  195. mdiobus_unregister(bus);
  196. return 0;
  197. }
  198. static const struct of_device_id ipq4019_mdio_dt_ids[] = {
  199. { .compatible = "qcom,ipq4019-mdio" },
  200. { .compatible = "qcom,ipq5018-mdio" },
  201. { }
  202. };
  203. MODULE_DEVICE_TABLE(of, ipq4019_mdio_dt_ids);
  204. static struct platform_driver ipq4019_mdio_driver = {
  205. .probe = ipq4019_mdio_probe,
  206. .remove = ipq4019_mdio_remove,
  207. .driver = {
  208. .name = "ipq4019-mdio",
  209. .of_match_table = ipq4019_mdio_dt_ids,
  210. },
  211. };
  212. module_platform_driver(ipq4019_mdio_driver);
  213. MODULE_DESCRIPTION("ipq4019 MDIO interface driver");
  214. MODULE_AUTHOR("Qualcomm Atheros");
  215. MODULE_LICENSE("Dual BSD/GPL");