bcm7xxx.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. * Broadcom BCM7xxx internal transceivers support.
  3. *
  4. * Copyright (C) 2014, Broadcom Corporation
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version
  9. * 2 of the License, or (at your option) any later version.
  10. */
  11. #include <linux/module.h>
  12. #include <linux/phy.h>
  13. #include <linux/delay.h>
  14. #include <linux/bitops.h>
  15. #include <linux/brcmphy.h>
  16. /* Broadcom BCM7xxx internal PHY registers */
  17. #define MII_BCM7XXX_CHANNEL_WIDTH 0x2000
  18. /* 40nm only register definitions */
  19. #define MII_BCM7XXX_100TX_AUX_CTL 0x10
  20. #define MII_BCM7XXX_100TX_FALSE_CAR 0x13
  21. #define MII_BCM7XXX_100TX_DISC 0x14
  22. #define MII_BCM7XXX_AUX_MODE 0x1d
  23. #define MII_BCM7XX_64CLK_MDIO BIT(12)
  24. #define MII_BCM7XXX_CORE_BASE1E 0x1e
  25. #define MII_BCM7XXX_TEST 0x1f
  26. #define MII_BCM7XXX_SHD_MODE_2 BIT(2)
  27. /* 28nm only register definitions */
  28. #define MISC_ADDR(base, channel) base, channel
  29. #define DSP_TAP10 MISC_ADDR(0x0a, 0)
  30. #define PLL_PLLCTRL_1 MISC_ADDR(0x32, 1)
  31. #define PLL_PLLCTRL_2 MISC_ADDR(0x32, 2)
  32. #define PLL_PLLCTRL_4 MISC_ADDR(0x33, 0)
  33. #define AFE_RXCONFIG_0 MISC_ADDR(0x38, 0)
  34. #define AFE_RXCONFIG_1 MISC_ADDR(0x38, 1)
  35. #define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3)
  36. #define AFE_TX_CONFIG MISC_ADDR(0x39, 0)
  37. #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0)
  38. #define CORE_EXPB0 0xb0
  39. static int bcm7445_config_init(struct phy_device *phydev)
  40. {
  41. int ret;
  42. const struct bcm7445_regs {
  43. int reg;
  44. u16 value;
  45. } bcm7445_regs_cfg[] = {
  46. /* increases ADC latency by 24ns */
  47. { MII_BCM54XX_EXP_SEL, 0x0038 },
  48. { MII_BCM54XX_EXP_DATA, 0xAB95 },
  49. /* increases internal 1V LDO voltage by 5% */
  50. { MII_BCM54XX_EXP_SEL, 0x2038 },
  51. { MII_BCM54XX_EXP_DATA, 0xBB22 },
  52. /* reduce RX low pass filter corner frequency */
  53. { MII_BCM54XX_EXP_SEL, 0x6038 },
  54. { MII_BCM54XX_EXP_DATA, 0xFFC5 },
  55. /* reduce RX high pass filter corner frequency */
  56. { MII_BCM54XX_EXP_SEL, 0x003a },
  57. { MII_BCM54XX_EXP_DATA, 0x2002 },
  58. };
  59. unsigned int i;
  60. for (i = 0; i < ARRAY_SIZE(bcm7445_regs_cfg); i++) {
  61. ret = phy_write(phydev,
  62. bcm7445_regs_cfg[i].reg,
  63. bcm7445_regs_cfg[i].value);
  64. if (ret)
  65. return ret;
  66. }
  67. return 0;
  68. }
  69. static void phy_write_exp(struct phy_device *phydev,
  70. u16 reg, u16 value)
  71. {
  72. phy_write(phydev, MII_BCM54XX_EXP_SEL, MII_BCM54XX_EXP_SEL_ER | reg);
  73. phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
  74. }
  75. static void phy_write_misc(struct phy_device *phydev,
  76. u16 reg, u16 chl, u16 value)
  77. {
  78. int tmp;
  79. phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
  80. tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
  81. tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
  82. phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
  83. tmp = (chl * MII_BCM7XXX_CHANNEL_WIDTH) | reg;
  84. phy_write(phydev, MII_BCM54XX_EXP_SEL, tmp);
  85. phy_write(phydev, MII_BCM54XX_EXP_DATA, value);
  86. }
  87. static int bcm7xxx_28nm_afe_config_init(struct phy_device *phydev)
  88. {
  89. /* Increase VCO range to prevent unlocking problem of PLL at low
  90. * temp
  91. */
  92. phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
  93. /* Change Ki to 011 */
  94. phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
  95. /* Disable loading of TVCO buffer to bandgap, set bandgap trim
  96. * to 111
  97. */
  98. phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
  99. /* Adjust bias current trim by -3 */
  100. phy_write_misc(phydev, DSP_TAP10, 0x690b);
  101. /* Switch to CORE_BASE1E */
  102. phy_write(phydev, MII_BCM7XXX_CORE_BASE1E, 0xd);
  103. /* Reset R_CAL/RC_CAL Engine */
  104. phy_write_exp(phydev, CORE_EXPB0, 0x0010);
  105. /* Disable Reset R_CAL/RC_CAL Engine */
  106. phy_write_exp(phydev, CORE_EXPB0, 0x0000);
  107. /* write AFE_RXCONFIG_0 */
  108. phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
  109. /* write AFE_RXCONFIG_1 */
  110. phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
  111. /* write AFE_RX_LP_COUNTER */
  112. phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
  113. /* write AFE_HPF_TRIM_OTHERS */
  114. phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
  115. /* write AFTE_TX_CONFIG */
  116. phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
  117. return 0;
  118. }
  119. static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
  120. {
  121. int ret;
  122. ret = bcm7445_config_init(phydev);
  123. if (ret)
  124. return ret;
  125. return bcm7xxx_28nm_afe_config_init(phydev);
  126. }
  127. static int bcm7xxx_28nm_resume(struct phy_device *phydev)
  128. {
  129. int ret;
  130. /* Re-apply workarounds coming out suspend/resume */
  131. ret = bcm7xxx_28nm_config_init(phydev);
  132. if (ret)
  133. return ret;
  134. /* 28nm Gigabit PHYs come out of reset without any half-duplex
  135. * or "hub" compliant advertised mode, fix that. This does not
  136. * cause any problems with the PHY library since genphy_config_aneg()
  137. * gracefully handles auto-negotiated and forced modes.
  138. */
  139. return genphy_config_aneg(phydev);
  140. }
  141. static int phy_set_clr_bits(struct phy_device *dev, int location,
  142. int set_mask, int clr_mask)
  143. {
  144. int v, ret;
  145. v = phy_read(dev, location);
  146. if (v < 0)
  147. return v;
  148. v &= ~clr_mask;
  149. v |= set_mask;
  150. ret = phy_write(dev, location, v);
  151. if (ret < 0)
  152. return ret;
  153. return v;
  154. }
  155. static int bcm7xxx_config_init(struct phy_device *phydev)
  156. {
  157. int ret;
  158. /* Enable 64 clock MDIO */
  159. phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO);
  160. phy_read(phydev, MII_BCM7XXX_AUX_MODE);
  161. /* Workaround only required for 100Mbits/sec */
  162. if (!(phydev->dev_flags & PHY_BRCM_100MBPS_WAR))
  163. return 0;
  164. /* set shadow mode 2 */
  165. ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
  166. MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
  167. if (ret < 0)
  168. return ret;
  169. /* set iddq_clkbias */
  170. phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
  171. udelay(10);
  172. /* reset iddq_clkbias */
  173. phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
  174. phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
  175. /* reset shadow mode 2 */
  176. ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0);
  177. if (ret < 0)
  178. return ret;
  179. return 0;
  180. }
  181. /* Workaround for putting the PHY in IDDQ mode, required
  182. * for all BCM7XXX 40nm and 65nm PHYs
  183. */
  184. static int bcm7xxx_suspend(struct phy_device *phydev)
  185. {
  186. int ret;
  187. const struct bcm7xxx_regs {
  188. int reg;
  189. u16 value;
  190. } bcm7xxx_suspend_cfg[] = {
  191. { MII_BCM7XXX_TEST, 0x008b },
  192. { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
  193. { MII_BCM7XXX_100TX_DISC, 0x7000 },
  194. { MII_BCM7XXX_TEST, 0x000f },
  195. { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
  196. { MII_BCM7XXX_TEST, 0x000b },
  197. };
  198. unsigned int i;
  199. for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
  200. ret = phy_write(phydev,
  201. bcm7xxx_suspend_cfg[i].reg,
  202. bcm7xxx_suspend_cfg[i].value);
  203. if (ret)
  204. return ret;
  205. }
  206. return 0;
  207. }
  208. static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
  209. {
  210. return 0;
  211. }
  212. static struct phy_driver bcm7xxx_driver[] = {
  213. {
  214. .phy_id = PHY_ID_BCM7366,
  215. .phy_id_mask = 0xfffffff0,
  216. .name = "Broadcom BCM7366",
  217. .features = PHY_GBIT_FEATURES |
  218. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  219. .flags = PHY_IS_INTERNAL,
  220. .config_init = bcm7xxx_28nm_afe_config_init,
  221. .config_aneg = genphy_config_aneg,
  222. .read_status = genphy_read_status,
  223. .resume = bcm7xxx_28nm_resume,
  224. .driver = { .owner = THIS_MODULE },
  225. }, {
  226. .phy_id = PHY_ID_BCM7439,
  227. .phy_id_mask = 0xfffffff0,
  228. .name = "Broadcom BCM7439",
  229. .features = PHY_GBIT_FEATURES |
  230. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  231. .flags = PHY_IS_INTERNAL,
  232. .config_init = bcm7xxx_28nm_afe_config_init,
  233. .config_aneg = genphy_config_aneg,
  234. .read_status = genphy_read_status,
  235. .resume = bcm7xxx_28nm_resume,
  236. .driver = { .owner = THIS_MODULE },
  237. }, {
  238. .phy_id = PHY_ID_BCM7445,
  239. .phy_id_mask = 0xfffffff0,
  240. .name = "Broadcom BCM7445",
  241. .features = PHY_GBIT_FEATURES |
  242. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  243. .flags = PHY_IS_INTERNAL,
  244. .config_init = bcm7xxx_28nm_config_init,
  245. .config_aneg = genphy_config_aneg,
  246. .read_status = genphy_read_status,
  247. .resume = bcm7xxx_28nm_afe_config_init,
  248. .driver = { .owner = THIS_MODULE },
  249. }, {
  250. .phy_id = PHY_BCM_OUI_4,
  251. .phy_id_mask = 0xffff0000,
  252. .name = "Broadcom BCM7XXX 40nm",
  253. .features = PHY_GBIT_FEATURES |
  254. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  255. .flags = PHY_IS_INTERNAL,
  256. .config_init = bcm7xxx_config_init,
  257. .config_aneg = genphy_config_aneg,
  258. .read_status = genphy_read_status,
  259. .suspend = bcm7xxx_suspend,
  260. .resume = bcm7xxx_config_init,
  261. .driver = { .owner = THIS_MODULE },
  262. }, {
  263. .phy_id = PHY_BCM_OUI_5,
  264. .phy_id_mask = 0xffffff00,
  265. .name = "Broadcom BCM7XXX 65nm",
  266. .features = PHY_BASIC_FEATURES |
  267. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  268. .flags = PHY_IS_INTERNAL,
  269. .config_init = bcm7xxx_dummy_config_init,
  270. .config_aneg = genphy_config_aneg,
  271. .read_status = genphy_read_status,
  272. .suspend = bcm7xxx_suspend,
  273. .resume = bcm7xxx_config_init,
  274. .driver = { .owner = THIS_MODULE },
  275. } };
  276. static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
  277. { PHY_ID_BCM7366, 0xfffffff0, },
  278. { PHY_ID_BCM7439, 0xfffffff0, },
  279. { PHY_ID_BCM7445, 0xfffffff0, },
  280. { PHY_BCM_OUI_4, 0xffff0000 },
  281. { PHY_BCM_OUI_5, 0xffffff00 },
  282. { }
  283. };
  284. static int __init bcm7xxx_phy_init(void)
  285. {
  286. return phy_drivers_register(bcm7xxx_driver,
  287. ARRAY_SIZE(bcm7xxx_driver));
  288. }
  289. static void __exit bcm7xxx_phy_exit(void)
  290. {
  291. phy_drivers_unregister(bcm7xxx_driver,
  292. ARRAY_SIZE(bcm7xxx_driver));
  293. }
  294. module_init(bcm7xxx_phy_init);
  295. module_exit(bcm7xxx_phy_exit);
  296. MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
  297. MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
  298. MODULE_LICENSE("GPL");
  299. MODULE_AUTHOR("Broadcom Corporation");