mscc.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /*
  2. * Driver for Microsemi VSC85xx PHYs
  3. *
  4. * Author: Nagaraju Lakkaraju
  5. * License: Dual MIT/GPL
  6. * Copyright (c) 2016 Microsemi Corporation
  7. */
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <linux/mdio.h>
  11. #include <linux/mii.h>
  12. #include <linux/phy.h>
  13. enum rgmii_rx_clock_delay {
  14. RGMII_RX_CLK_DELAY_0_2_NS = 0,
  15. RGMII_RX_CLK_DELAY_0_8_NS = 1,
  16. RGMII_RX_CLK_DELAY_1_1_NS = 2,
  17. RGMII_RX_CLK_DELAY_1_7_NS = 3,
  18. RGMII_RX_CLK_DELAY_2_0_NS = 4,
  19. RGMII_RX_CLK_DELAY_2_3_NS = 5,
  20. RGMII_RX_CLK_DELAY_2_6_NS = 6,
  21. RGMII_RX_CLK_DELAY_3_4_NS = 7
  22. };
  23. #define MII_VSC85XX_INT_MASK 25
  24. #define MII_VSC85XX_INT_MASK_MASK 0xa000
  25. #define MII_VSC85XX_INT_STATUS 26
  26. #define MSCC_EXT_PAGE_ACCESS 31
  27. #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
  28. #define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */
  29. /* Extended Page 2 Registers */
  30. #define MSCC_PHY_RGMII_CNTL 20
  31. #define RGMII_RX_CLK_DELAY_MASK 0x0070
  32. #define RGMII_RX_CLK_DELAY_POS 4
  33. /* Microsemi PHY ID's */
  34. #define PHY_ID_VSC8531 0x00070570
  35. #define PHY_ID_VSC8541 0x00070770
  36. static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page)
  37. {
  38. int rc;
  39. rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
  40. return rc;
  41. }
  42. static int vsc85xx_default_config(struct phy_device *phydev)
  43. {
  44. int rc;
  45. u16 reg_val;
  46. mutex_lock(&phydev->lock);
  47. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
  48. if (rc != 0)
  49. goto out_unlock;
  50. reg_val = phy_read(phydev, MSCC_PHY_RGMII_CNTL);
  51. reg_val &= ~(RGMII_RX_CLK_DELAY_MASK);
  52. reg_val |= (RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS);
  53. phy_write(phydev, MSCC_PHY_RGMII_CNTL, reg_val);
  54. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
  55. out_unlock:
  56. mutex_unlock(&phydev->lock);
  57. return rc;
  58. }
  59. static int vsc85xx_config_init(struct phy_device *phydev)
  60. {
  61. int rc;
  62. rc = vsc85xx_default_config(phydev);
  63. if (rc)
  64. return rc;
  65. rc = genphy_config_init(phydev);
  66. return rc;
  67. }
  68. static int vsc85xx_ack_interrupt(struct phy_device *phydev)
  69. {
  70. int rc = 0;
  71. if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
  72. rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
  73. return (rc < 0) ? rc : 0;
  74. }
  75. static int vsc85xx_config_intr(struct phy_device *phydev)
  76. {
  77. int rc;
  78. if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
  79. rc = phy_write(phydev, MII_VSC85XX_INT_MASK,
  80. MII_VSC85XX_INT_MASK_MASK);
  81. } else {
  82. rc = phy_write(phydev, MII_VSC85XX_INT_MASK, 0);
  83. if (rc < 0)
  84. return rc;
  85. rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
  86. }
  87. return rc;
  88. }
  89. /* Microsemi VSC85xx PHYs */
  90. static struct phy_driver vsc85xx_driver[] = {
  91. {
  92. .phy_id = PHY_ID_VSC8531,
  93. .name = "Microsemi VSC8531",
  94. .phy_id_mask = 0xfffffff0,
  95. .features = PHY_GBIT_FEATURES,
  96. .flags = PHY_HAS_INTERRUPT,
  97. .soft_reset = &genphy_soft_reset,
  98. .config_init = &vsc85xx_config_init,
  99. .config_aneg = &genphy_config_aneg,
  100. .aneg_done = &genphy_aneg_done,
  101. .read_status = &genphy_read_status,
  102. .ack_interrupt = &vsc85xx_ack_interrupt,
  103. .config_intr = &vsc85xx_config_intr,
  104. .suspend = &genphy_suspend,
  105. .resume = &genphy_resume,
  106. },
  107. {
  108. .phy_id = PHY_ID_VSC8541,
  109. .name = "Microsemi VSC8541 SyncE",
  110. .phy_id_mask = 0xfffffff0,
  111. .features = PHY_GBIT_FEATURES,
  112. .flags = PHY_HAS_INTERRUPT,
  113. .soft_reset = &genphy_soft_reset,
  114. .config_init = &vsc85xx_config_init,
  115. .config_aneg = &genphy_config_aneg,
  116. .aneg_done = &genphy_aneg_done,
  117. .read_status = &genphy_read_status,
  118. .ack_interrupt = &vsc85xx_ack_interrupt,
  119. .config_intr = &vsc85xx_config_intr,
  120. .suspend = &genphy_suspend,
  121. .resume = &genphy_resume,
  122. }
  123. };
  124. module_phy_driver(vsc85xx_driver);
  125. static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
  126. { PHY_ID_VSC8531, 0xfffffff0, },
  127. { PHY_ID_VSC8541, 0xfffffff0, },
  128. { }
  129. };
  130. MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
  131. MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
  132. MODULE_AUTHOR("Nagaraju Lakkaraju");
  133. MODULE_LICENSE("Dual MIT/GPL");