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 phy_set_clr_bits(struct phy_device *dev, int location,
  128. int set_mask, int clr_mask)
  129. {
  130. int v, ret;
  131. v = phy_read(dev, location);
  132. if (v < 0)
  133. return v;
  134. v &= ~clr_mask;
  135. v |= set_mask;
  136. ret = phy_write(dev, location, v);
  137. if (ret < 0)
  138. return ret;
  139. return v;
  140. }
  141. static int bcm7xxx_config_init(struct phy_device *phydev)
  142. {
  143. int ret;
  144. /* Enable 64 clock MDIO */
  145. phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XX_64CLK_MDIO);
  146. phy_read(phydev, MII_BCM7XXX_AUX_MODE);
  147. /* Workaround only required for 100Mbits/sec */
  148. if (!(phydev->dev_flags & PHY_BRCM_100MBPS_WAR))
  149. return 0;
  150. /* set shadow mode 2 */
  151. ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
  152. MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
  153. if (ret < 0)
  154. return ret;
  155. /* set iddq_clkbias */
  156. phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
  157. udelay(10);
  158. /* reset iddq_clkbias */
  159. phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
  160. phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
  161. /* reset shadow mode 2 */
  162. ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, MII_BCM7XXX_SHD_MODE_2, 0);
  163. if (ret < 0)
  164. return ret;
  165. return 0;
  166. }
  167. /* Workaround for putting the PHY in IDDQ mode, required
  168. * for all BCM7XXX PHYs
  169. */
  170. static int bcm7xxx_suspend(struct phy_device *phydev)
  171. {
  172. int ret;
  173. const struct bcm7xxx_regs {
  174. int reg;
  175. u16 value;
  176. } bcm7xxx_suspend_cfg[] = {
  177. { MII_BCM7XXX_TEST, 0x008b },
  178. { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
  179. { MII_BCM7XXX_100TX_DISC, 0x7000 },
  180. { MII_BCM7XXX_TEST, 0x000f },
  181. { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
  182. { MII_BCM7XXX_TEST, 0x000b },
  183. };
  184. unsigned int i;
  185. for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
  186. ret = phy_write(phydev,
  187. bcm7xxx_suspend_cfg[i].reg,
  188. bcm7xxx_suspend_cfg[i].value);
  189. if (ret)
  190. return ret;
  191. }
  192. return 0;
  193. }
  194. static int bcm7xxx_dummy_config_init(struct phy_device *phydev)
  195. {
  196. return 0;
  197. }
  198. static struct phy_driver bcm7xxx_driver[] = {
  199. {
  200. .phy_id = PHY_ID_BCM7366,
  201. .phy_id_mask = 0xfffffff0,
  202. .name = "Broadcom BCM7366",
  203. .features = PHY_GBIT_FEATURES |
  204. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  205. .flags = PHY_IS_INTERNAL,
  206. .config_init = bcm7xxx_28nm_afe_config_init,
  207. .config_aneg = genphy_config_aneg,
  208. .read_status = genphy_read_status,
  209. .suspend = bcm7xxx_suspend,
  210. .resume = bcm7xxx_28nm_afe_config_init,
  211. .driver = { .owner = THIS_MODULE },
  212. }, {
  213. .phy_id = PHY_ID_BCM7439,
  214. .phy_id_mask = 0xfffffff0,
  215. .name = "Broadcom BCM7439",
  216. .features = PHY_GBIT_FEATURES |
  217. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  218. .flags = PHY_IS_INTERNAL,
  219. .config_init = bcm7xxx_28nm_afe_config_init,
  220. .config_aneg = genphy_config_aneg,
  221. .read_status = genphy_read_status,
  222. .suspend = bcm7xxx_suspend,
  223. .resume = bcm7xxx_28nm_afe_config_init,
  224. .driver = { .owner = THIS_MODULE },
  225. }, {
  226. .phy_id = PHY_ID_BCM7445,
  227. .phy_id_mask = 0xfffffff0,
  228. .name = "Broadcom BCM7445",
  229. .features = PHY_GBIT_FEATURES |
  230. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  231. .flags = PHY_IS_INTERNAL,
  232. .config_init = bcm7xxx_28nm_config_init,
  233. .config_aneg = genphy_config_aneg,
  234. .read_status = genphy_read_status,
  235. .suspend = bcm7xxx_suspend,
  236. .resume = bcm7xxx_28nm_config_init,
  237. .driver = { .owner = THIS_MODULE },
  238. }, {
  239. .name = "Broadcom BCM7XXX 28nm",
  240. .phy_id = PHY_ID_BCM7XXX_28,
  241. .phy_id_mask = PHY_BCM_OUI_MASK,
  242. .features = PHY_GBIT_FEATURES |
  243. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  244. .flags = PHY_IS_INTERNAL,
  245. .config_init = bcm7xxx_28nm_config_init,
  246. .config_aneg = genphy_config_aneg,
  247. .read_status = genphy_read_status,
  248. .suspend = bcm7xxx_suspend,
  249. .resume = bcm7xxx_28nm_config_init,
  250. .driver = { .owner = THIS_MODULE },
  251. }, {
  252. .phy_id = PHY_BCM_OUI_4,
  253. .phy_id_mask = 0xffff0000,
  254. .name = "Broadcom BCM7XXX 40nm",
  255. .features = PHY_GBIT_FEATURES |
  256. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  257. .flags = PHY_IS_INTERNAL,
  258. .config_init = bcm7xxx_config_init,
  259. .config_aneg = genphy_config_aneg,
  260. .read_status = genphy_read_status,
  261. .suspend = bcm7xxx_suspend,
  262. .resume = bcm7xxx_config_init,
  263. .driver = { .owner = THIS_MODULE },
  264. }, {
  265. .phy_id = PHY_BCM_OUI_5,
  266. .phy_id_mask = 0xffffff00,
  267. .name = "Broadcom BCM7XXX 65nm",
  268. .features = PHY_BASIC_FEATURES |
  269. SUPPORTED_Pause | SUPPORTED_Asym_Pause,
  270. .flags = PHY_IS_INTERNAL,
  271. .config_init = bcm7xxx_dummy_config_init,
  272. .config_aneg = genphy_config_aneg,
  273. .read_status = genphy_read_status,
  274. .suspend = bcm7xxx_suspend,
  275. .resume = bcm7xxx_config_init,
  276. .driver = { .owner = THIS_MODULE },
  277. } };
  278. static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
  279. { PHY_ID_BCM7366, 0xfffffff0, },
  280. { PHY_ID_BCM7439, 0xfffffff0, },
  281. { PHY_ID_BCM7445, 0xfffffff0, },
  282. { PHY_ID_BCM7XXX_28, 0xfffffc00 },
  283. { PHY_BCM_OUI_4, 0xffff0000 },
  284. { PHY_BCM_OUI_5, 0xffffff00 },
  285. { }
  286. };
  287. static int __init bcm7xxx_phy_init(void)
  288. {
  289. return phy_drivers_register(bcm7xxx_driver,
  290. ARRAY_SIZE(bcm7xxx_driver));
  291. }
  292. static void __exit bcm7xxx_phy_exit(void)
  293. {
  294. phy_drivers_unregister(bcm7xxx_driver,
  295. ARRAY_SIZE(bcm7xxx_driver));
  296. }
  297. module_init(bcm7xxx_phy_init);
  298. module_exit(bcm7xxx_phy_exit);
  299. MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
  300. MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
  301. MODULE_LICENSE("GPL");
  302. MODULE_AUTHOR("Broadcom Corporation");