bcm-cygnus.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2015 Broadcom Corporation
  4. */
  5. /* Broadcom Cygnus SoC internal transceivers support. */
  6. #include "bcm-phy-lib.h"
  7. #include <linux/brcmphy.h>
  8. #include <linux/module.h>
  9. #include <linux/netdevice.h>
  10. #include <linux/phy.h>
  11. struct bcm_omega_phy_priv {
  12. u64 *stats;
  13. };
  14. /* Broadcom Cygnus Phy specific registers */
  15. #define MII_BCM_CYGNUS_AFE_VDAC_ICTRL_0 0x91E5 /* VDAL Control register */
  16. static int bcm_cygnus_afe_config(struct phy_device *phydev)
  17. {
  18. int rc;
  19. /* ensure smdspclk is enabled */
  20. rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, 0x0c30);
  21. if (rc < 0)
  22. return rc;
  23. /* AFE_VDAC_ICTRL_0 bit 7:4 Iq=1100 for 1g 10bt, normal modes */
  24. rc = bcm_phy_write_misc(phydev, 0x39, 0x01, 0xA7C8);
  25. if (rc < 0)
  26. return rc;
  27. /* AFE_HPF_TRIM_OTHERS bit11=1, short cascode enable for all modes*/
  28. rc = bcm_phy_write_misc(phydev, 0x3A, 0x00, 0x0803);
  29. if (rc < 0)
  30. return rc;
  31. /* AFE_TX_CONFIG_1 bit 7:4 Iq=1100 for test modes */
  32. rc = bcm_phy_write_misc(phydev, 0x3A, 0x01, 0xA740);
  33. if (rc < 0)
  34. return rc;
  35. /* AFE TEMPSEN_OTHERS rcal_HT, rcal_LT 10000 */
  36. rc = bcm_phy_write_misc(phydev, 0x3A, 0x03, 0x8400);
  37. if (rc < 0)
  38. return rc;
  39. /* AFE_FUTURE_RSV bit 2:0 rccal <2:0>=100 */
  40. rc = bcm_phy_write_misc(phydev, 0x3B, 0x00, 0x0004);
  41. if (rc < 0)
  42. return rc;
  43. /* Adjust bias current trim to overcome digital offSet */
  44. rc = phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x02);
  45. if (rc < 0)
  46. return rc;
  47. /* make rcal=100, since rdb default is 000 */
  48. rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB1, 0x10);
  49. if (rc < 0)
  50. return rc;
  51. /* CORE_EXPB0, Reset R_CAL/RC_CAL Engine */
  52. rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x10);
  53. if (rc < 0)
  54. return rc;
  55. /* CORE_EXPB0, Disable Reset R_CAL/RC_CAL Engine */
  56. rc = bcm_phy_write_exp_sel(phydev, MII_BRCM_CORE_EXPB0, 0x00);
  57. return 0;
  58. }
  59. static int bcm_cygnus_config_init(struct phy_device *phydev)
  60. {
  61. int reg, rc;
  62. reg = phy_read(phydev, MII_BCM54XX_ECR);
  63. if (reg < 0)
  64. return reg;
  65. /* Mask interrupts globally. */
  66. reg |= MII_BCM54XX_ECR_IM;
  67. rc = phy_write(phydev, MII_BCM54XX_ECR, reg);
  68. if (rc)
  69. return rc;
  70. /* Unmask events of interest */
  71. reg = ~(MII_BCM54XX_INT_DUPLEX |
  72. MII_BCM54XX_INT_SPEED |
  73. MII_BCM54XX_INT_LINK);
  74. rc = phy_write(phydev, MII_BCM54XX_IMR, reg);
  75. if (rc)
  76. return rc;
  77. /* Apply AFE settings for the PHY */
  78. rc = bcm_cygnus_afe_config(phydev);
  79. if (rc)
  80. return rc;
  81. /* Advertise EEE */
  82. rc = bcm_phy_set_eee(phydev, true);
  83. if (rc)
  84. return rc;
  85. /* Enable APD */
  86. return bcm_phy_enable_apd(phydev, false);
  87. }
  88. static int bcm_cygnus_resume(struct phy_device *phydev)
  89. {
  90. int rc;
  91. genphy_resume(phydev);
  92. /* Re-initialize the PHY to apply AFE work-arounds and
  93. * configurations when coming out of suspend.
  94. */
  95. rc = bcm_cygnus_config_init(phydev);
  96. if (rc)
  97. return rc;
  98. /* restart auto negotiation with the new settings */
  99. return genphy_config_aneg(phydev);
  100. }
  101. static int bcm_omega_config_init(struct phy_device *phydev)
  102. {
  103. u8 count, rev;
  104. int ret = 0;
  105. rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
  106. pr_info_once("%s: %s PHY revision: 0x%02x\n",
  107. phydev_name(phydev), phydev->drv->name, rev);
  108. /* Dummy read to a register to workaround an issue upon reset where the
  109. * internal inverter may not allow the first MDIO transaction to pass
  110. * the MDIO management controller and make us return 0xffff for such
  111. * reads.
  112. */
  113. phy_read(phydev, MII_BMSR);
  114. switch (rev) {
  115. case 0x00:
  116. ret = bcm_phy_28nm_a0b0_afe_config_init(phydev);
  117. break;
  118. default:
  119. break;
  120. }
  121. if (ret)
  122. return ret;
  123. ret = bcm_phy_downshift_get(phydev, &count);
  124. if (ret)
  125. return ret;
  126. /* Only enable EEE if Wirespeed/downshift is disabled */
  127. ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
  128. if (ret)
  129. return ret;
  130. return bcm_phy_enable_apd(phydev, true);
  131. }
  132. static int bcm_omega_resume(struct phy_device *phydev)
  133. {
  134. int ret;
  135. /* Re-apply workarounds coming out suspend/resume */
  136. ret = bcm_omega_config_init(phydev);
  137. if (ret)
  138. return ret;
  139. /* 28nm Gigabit PHYs come out of reset without any half-duplex
  140. * or "hub" compliant advertised mode, fix that. This does not
  141. * cause any problems with the PHY library since genphy_config_aneg()
  142. * gracefully handles auto-negotiated and forced modes.
  143. */
  144. return genphy_config_aneg(phydev);
  145. }
  146. static int bcm_omega_get_tunable(struct phy_device *phydev,
  147. struct ethtool_tunable *tuna, void *data)
  148. {
  149. switch (tuna->id) {
  150. case ETHTOOL_PHY_DOWNSHIFT:
  151. return bcm_phy_downshift_get(phydev, (u8 *)data);
  152. default:
  153. return -EOPNOTSUPP;
  154. }
  155. }
  156. static int bcm_omega_set_tunable(struct phy_device *phydev,
  157. struct ethtool_tunable *tuna,
  158. const void *data)
  159. {
  160. u8 count = *(u8 *)data;
  161. int ret;
  162. switch (tuna->id) {
  163. case ETHTOOL_PHY_DOWNSHIFT:
  164. ret = bcm_phy_downshift_set(phydev, count);
  165. break;
  166. default:
  167. return -EOPNOTSUPP;
  168. }
  169. if (ret)
  170. return ret;
  171. /* Disable EEE advertisement since this prevents the PHY
  172. * from successfully linking up, trigger auto-negotiation restart
  173. * to let the MAC decide what to do.
  174. */
  175. ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
  176. if (ret)
  177. return ret;
  178. return genphy_restart_aneg(phydev);
  179. }
  180. static void bcm_omega_get_phy_stats(struct phy_device *phydev,
  181. struct ethtool_stats *stats, u64 *data)
  182. {
  183. struct bcm_omega_phy_priv *priv = phydev->priv;
  184. bcm_phy_get_stats(phydev, priv->stats, stats, data);
  185. }
  186. static int bcm_omega_probe(struct phy_device *phydev)
  187. {
  188. struct bcm_omega_phy_priv *priv;
  189. priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
  190. if (!priv)
  191. return -ENOMEM;
  192. phydev->priv = priv;
  193. priv->stats = devm_kcalloc(&phydev->mdio.dev,
  194. bcm_phy_get_sset_count(phydev), sizeof(u64),
  195. GFP_KERNEL);
  196. if (!priv->stats)
  197. return -ENOMEM;
  198. return 0;
  199. }
  200. static struct phy_driver bcm_cygnus_phy_driver[] = {
  201. {
  202. .phy_id = PHY_ID_BCM_CYGNUS,
  203. .phy_id_mask = 0xfffffff0,
  204. .name = "Broadcom Cygnus PHY",
  205. /* PHY_GBIT_FEATURES */
  206. .config_init = bcm_cygnus_config_init,
  207. .ack_interrupt = bcm_phy_ack_intr,
  208. .config_intr = bcm_phy_config_intr,
  209. .suspend = genphy_suspend,
  210. .resume = bcm_cygnus_resume,
  211. }, {
  212. .phy_id = PHY_ID_BCM_OMEGA,
  213. .phy_id_mask = 0xfffffff0,
  214. .name = "Broadcom Omega Combo GPHY",
  215. /* PHY_GBIT_FEATURES */
  216. .flags = PHY_IS_INTERNAL,
  217. .config_init = bcm_omega_config_init,
  218. .suspend = genphy_suspend,
  219. .resume = bcm_omega_resume,
  220. .get_tunable = bcm_omega_get_tunable,
  221. .set_tunable = bcm_omega_set_tunable,
  222. .get_sset_count = bcm_phy_get_sset_count,
  223. .get_strings = bcm_phy_get_strings,
  224. .get_stats = bcm_omega_get_phy_stats,
  225. .probe = bcm_omega_probe,
  226. }
  227. };
  228. static struct mdio_device_id __maybe_unused bcm_cygnus_phy_tbl[] = {
  229. { PHY_ID_BCM_CYGNUS, 0xfffffff0, },
  230. { PHY_ID_BCM_OMEGA, 0xfffffff0, },
  231. { }
  232. };
  233. MODULE_DEVICE_TABLE(mdio, bcm_cygnus_phy_tbl);
  234. module_phy_driver(bcm_cygnus_phy_driver);
  235. MODULE_DESCRIPTION("Broadcom Cygnus internal PHY driver");
  236. MODULE_LICENSE("GPL v2");
  237. MODULE_AUTHOR("Broadcom Corporation");