bcm-phy-lib.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Copyright (C) 2015 Broadcom Corporation
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation version 2.
  7. *
  8. * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  9. * kind, whether express or implied; without even the implied warranty
  10. * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. */
  13. #include "bcm-phy-lib.h"
  14. #include <linux/brcmphy.h>
  15. #include <linux/export.h>
  16. #include <linux/mdio.h>
  17. #include <linux/module.h>
  18. #include <linux/phy.h>
  19. #define MII_BCM_CHANNEL_WIDTH 0x2000
  20. #define BCM_CL45VEN_EEE_ADV 0x3c
  21. int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
  22. {
  23. int rc;
  24. rc = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
  25. if (rc < 0)
  26. return rc;
  27. return phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
  28. }
  29. EXPORT_SYMBOL_GPL(bcm_phy_write_exp);
  30. int bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
  31. {
  32. int val;
  33. val = phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
  34. if (val < 0)
  35. return val;
  36. val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
  37. /* Restore default value. It's O.K. if this write fails. */
  38. phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
  39. return val;
  40. }
  41. EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
  42. int bcm_phy_write_misc(struct phy_device *phydev,
  43. u16 reg, u16 chl, u16 val)
  44. {
  45. int rc;
  46. int tmp;
  47. rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
  48. MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
  49. if (rc < 0)
  50. return rc;
  51. tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
  52. tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
  53. rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
  54. if (rc < 0)
  55. return rc;
  56. tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
  57. rc = bcm_phy_write_exp(phydev, tmp, val);
  58. return rc;
  59. }
  60. EXPORT_SYMBOL_GPL(bcm_phy_write_misc);
  61. int bcm_phy_read_misc(struct phy_device *phydev,
  62. u16 reg, u16 chl)
  63. {
  64. int rc;
  65. int tmp;
  66. rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
  67. MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
  68. if (rc < 0)
  69. return rc;
  70. tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
  71. tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
  72. rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
  73. if (rc < 0)
  74. return rc;
  75. tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
  76. rc = bcm_phy_read_exp(phydev, tmp);
  77. return rc;
  78. }
  79. EXPORT_SYMBOL_GPL(bcm_phy_read_misc);
  80. int bcm_phy_ack_intr(struct phy_device *phydev)
  81. {
  82. int reg;
  83. /* Clear pending interrupts. */
  84. reg = phy_read(phydev, MII_BCM54XX_ISR);
  85. if (reg < 0)
  86. return reg;
  87. return 0;
  88. }
  89. EXPORT_SYMBOL_GPL(bcm_phy_ack_intr);
  90. int bcm_phy_config_intr(struct phy_device *phydev)
  91. {
  92. int reg;
  93. reg = phy_read(phydev, MII_BCM54XX_ECR);
  94. if (reg < 0)
  95. return reg;
  96. if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
  97. reg &= ~MII_BCM54XX_ECR_IM;
  98. else
  99. reg |= MII_BCM54XX_ECR_IM;
  100. return phy_write(phydev, MII_BCM54XX_ECR, reg);
  101. }
  102. EXPORT_SYMBOL_GPL(bcm_phy_config_intr);
  103. int bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
  104. {
  105. phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
  106. return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
  107. }
  108. EXPORT_SYMBOL_GPL(bcm_phy_read_shadow);
  109. int bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
  110. u16 val)
  111. {
  112. return phy_write(phydev, MII_BCM54XX_SHD,
  113. MII_BCM54XX_SHD_WRITE |
  114. MII_BCM54XX_SHD_VAL(shadow) |
  115. MII_BCM54XX_SHD_DATA(val));
  116. }
  117. EXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
  118. int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
  119. {
  120. int val;
  121. if (dll_pwr_down) {
  122. val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
  123. if (val < 0)
  124. return val;
  125. val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
  126. bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
  127. }
  128. val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
  129. if (val < 0)
  130. return val;
  131. /* Clear APD bits */
  132. val &= BCM_APD_CLR_MASK;
  133. if (phydev->autoneg == AUTONEG_ENABLE)
  134. val |= BCM54XX_SHD_APD_EN;
  135. else
  136. val |= BCM_NO_ANEG_APD_EN;
  137. /* Enable energy detect single link pulse for easy wakeup */
  138. val |= BCM_APD_SINGLELP_EN;
  139. /* Enable Auto Power-Down (APD) for the PHY */
  140. return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
  141. }
  142. EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
  143. int bcm_phy_enable_eee(struct phy_device *phydev)
  144. {
  145. int val;
  146. /* Enable EEE at PHY level */
  147. val = phy_read_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
  148. MDIO_MMD_AN, phydev->addr);
  149. if (val < 0)
  150. return val;
  151. val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
  152. phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
  153. MDIO_MMD_AN, phydev->addr, (u32)val);
  154. /* Advertise EEE */
  155. val = phy_read_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
  156. MDIO_MMD_AN, phydev->addr);
  157. if (val < 0)
  158. return val;
  159. val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
  160. phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
  161. MDIO_MMD_AN, phydev->addr, (u32)val);
  162. return 0;
  163. }
  164. EXPORT_SYMBOL_GPL(bcm_phy_enable_eee);
  165. MODULE_DESCRIPTION("Broadcom PHY Library");
  166. MODULE_LICENSE("GPL v2");
  167. MODULE_AUTHOR("Broadcom Corporation");