ncn26000.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
  2. /*
  3. * Driver for the onsemi 10BASE-T1S NCN26000 PHYs family.
  4. *
  5. * Copyright 2022 onsemi
  6. */
  7. #include <linux/kernel.h>
  8. #include <linux/bitfield.h>
  9. #include <linux/errno.h>
  10. #include <linux/init.h>
  11. #include <linux/module.h>
  12. #include <linux/mii.h>
  13. #include <linux/phy.h>
  14. #include "mdio-open-alliance.h"
  15. #define PHY_ID_NCN26000 0x180FF5A1
  16. #define NCN26000_REG_IRQ_CTL 16
  17. #define NCN26000_REG_IRQ_STATUS 17
  18. // the NCN26000 maps link_ctrl to BMCR_ANENABLE
  19. #define NCN26000_BCMR_LINK_CTRL_BIT BMCR_ANENABLE
  20. // the NCN26000 maps link_status to BMSR_ANEGCOMPLETE
  21. #define NCN26000_BMSR_LINK_STATUS_BIT BMSR_ANEGCOMPLETE
  22. #define NCN26000_IRQ_LINKST_BIT BIT(0)
  23. #define NCN26000_IRQ_PLCAST_BIT BIT(1)
  24. #define NCN26000_IRQ_LJABBER_BIT BIT(2)
  25. #define NCN26000_IRQ_RJABBER_BIT BIT(3)
  26. #define NCN26000_IRQ_PLCAREC_BIT BIT(4)
  27. #define NCN26000_IRQ_PHYSCOL_BIT BIT(5)
  28. #define NCN26000_IRQ_RESET_BIT BIT(15)
  29. #define TO_TMR_DEFAULT 32
  30. static int ncn26000_config_init(struct phy_device *phydev)
  31. {
  32. /* HW bug workaround: the default value of the PLCA TO_TIMER should be
  33. * 32, where the current version of NCN26000 reports 24. This will be
  34. * fixed in future PHY versions. For the time being, we force the
  35. * correct default here.
  36. */
  37. return phy_write_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_PLCA_TOTMR,
  38. TO_TMR_DEFAULT);
  39. }
  40. static int ncn26000_config_aneg(struct phy_device *phydev)
  41. {
  42. /* Note: the NCN26000 supports only P2MP link mode. Therefore, AN is not
  43. * supported. However, this function is invoked by phylib to enable the
  44. * PHY, regardless of the AN support.
  45. */
  46. phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
  47. phydev->mdix = ETH_TP_MDI;
  48. // bring up the link
  49. return phy_write(phydev, MII_BMCR, NCN26000_BCMR_LINK_CTRL_BIT);
  50. }
  51. static int ncn26000_read_status(struct phy_device *phydev)
  52. {
  53. /* The NCN26000 reports NCN26000_LINK_STATUS_BIT if the link status of
  54. * the PHY is up. It further reports the logical AND of the link status
  55. * and the PLCA status in the BMSR_LSTATUS bit.
  56. */
  57. int ret;
  58. /* The link state is latched low so that momentary link
  59. * drops can be detected. Do not double-read the status
  60. * in polling mode to detect such short link drops except
  61. * the link was already down.
  62. */
  63. if (!phy_polling_mode(phydev) || !phydev->link) {
  64. ret = phy_read(phydev, MII_BMSR);
  65. if (ret < 0)
  66. return ret;
  67. else if (ret & NCN26000_BMSR_LINK_STATUS_BIT)
  68. goto upd_link;
  69. }
  70. ret = phy_read(phydev, MII_BMSR);
  71. if (ret < 0)
  72. return ret;
  73. upd_link:
  74. // update link status
  75. if (ret & NCN26000_BMSR_LINK_STATUS_BIT) {
  76. phydev->link = 1;
  77. phydev->pause = 0;
  78. phydev->duplex = DUPLEX_HALF;
  79. phydev->speed = SPEED_10;
  80. } else {
  81. phydev->link = 0;
  82. phydev->duplex = DUPLEX_UNKNOWN;
  83. phydev->speed = SPEED_UNKNOWN;
  84. }
  85. return 0;
  86. }
  87. static irqreturn_t ncn26000_handle_interrupt(struct phy_device *phydev)
  88. {
  89. int ret;
  90. // read and aknowledge the IRQ status register
  91. ret = phy_read(phydev, NCN26000_REG_IRQ_STATUS);
  92. // check only link status changes
  93. if (ret < 0 || (ret & NCN26000_REG_IRQ_STATUS) == 0)
  94. return IRQ_NONE;
  95. phy_trigger_machine(phydev);
  96. return IRQ_HANDLED;
  97. }
  98. static int ncn26000_config_intr(struct phy_device *phydev)
  99. {
  100. int ret;
  101. u16 irqe;
  102. if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
  103. // acknowledge IRQs
  104. ret = phy_read(phydev, NCN26000_REG_IRQ_STATUS);
  105. if (ret < 0)
  106. return ret;
  107. // get link status notifications
  108. irqe = NCN26000_IRQ_LINKST_BIT;
  109. } else {
  110. // disable all IRQs
  111. irqe = 0;
  112. }
  113. ret = phy_write(phydev, NCN26000_REG_IRQ_CTL, irqe);
  114. if (ret != 0)
  115. return ret;
  116. return 0;
  117. }
  118. static struct phy_driver ncn26000_driver[] = {
  119. {
  120. PHY_ID_MATCH_MODEL(PHY_ID_NCN26000),
  121. .name = "NCN26000",
  122. .features = PHY_BASIC_T1S_P2MP_FEATURES,
  123. .config_init = ncn26000_config_init,
  124. .config_intr = ncn26000_config_intr,
  125. .config_aneg = ncn26000_config_aneg,
  126. .read_status = ncn26000_read_status,
  127. .handle_interrupt = ncn26000_handle_interrupt,
  128. .get_plca_cfg = genphy_c45_plca_get_cfg,
  129. .set_plca_cfg = genphy_c45_plca_set_cfg,
  130. .get_plca_status = genphy_c45_plca_get_status,
  131. .soft_reset = genphy_soft_reset,
  132. },
  133. };
  134. module_phy_driver(ncn26000_driver);
  135. static const struct mdio_device_id __maybe_unused ncn26000_tbl[] = {
  136. { PHY_ID_MATCH_MODEL(PHY_ID_NCN26000) },
  137. { }
  138. };
  139. MODULE_DEVICE_TABLE(mdio, ncn26000_tbl);
  140. MODULE_AUTHOR("Piergiorgio Beruto");
  141. MODULE_DESCRIPTION("onsemi 10BASE-T1S PHY driver");
  142. MODULE_LICENSE("Dual BSD/GPL");