bcm7xxx.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  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 "bcm-phy-lib.h"
  15. #include <linux/bitops.h>
  16. #include <linux/brcmphy.h>
  17. #include <linux/mdio.h>
  18. /* Broadcom BCM7xxx internal PHY registers */
  19. /* 40nm only register definitions */
  20. #define MII_BCM7XXX_100TX_AUX_CTL 0x10
  21. #define MII_BCM7XXX_100TX_FALSE_CAR 0x13
  22. #define MII_BCM7XXX_100TX_DISC 0x14
  23. #define MII_BCM7XXX_AUX_MODE 0x1d
  24. #define MII_BCM7XXX_64CLK_MDIO BIT(12)
  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_RXCONFIG_2 MISC_ADDR(0x38, 2)
  36. #define AFE_RX_LP_COUNTER MISC_ADDR(0x38, 3)
  37. #define AFE_TX_CONFIG MISC_ADDR(0x39, 0)
  38. #define AFE_VDCA_ICTRL_0 MISC_ADDR(0x39, 1)
  39. #define AFE_VDAC_OTHERS_0 MISC_ADDR(0x39, 3)
  40. #define AFE_HPF_TRIM_OTHERS MISC_ADDR(0x3a, 0)
  41. struct bcm7xxx_phy_priv {
  42. u64 *stats;
  43. };
  44. static void r_rc_cal_reset(struct phy_device *phydev)
  45. {
  46. /* Reset R_CAL/RC_CAL Engine */
  47. bcm_phy_write_exp(phydev, 0x00b0, 0x0010);
  48. /* Disable Reset R_AL/RC_CAL Engine */
  49. bcm_phy_write_exp(phydev, 0x00b0, 0x0000);
  50. }
  51. static int bcm7xxx_28nm_b0_afe_config_init(struct phy_device *phydev)
  52. {
  53. /* Increase VCO range to prevent unlocking problem of PLL at low
  54. * temp
  55. */
  56. bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
  57. /* Change Ki to 011 */
  58. bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
  59. /* Disable loading of TVCO buffer to bandgap, set bandgap trim
  60. * to 111
  61. */
  62. bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
  63. /* Adjust bias current trim by -3 */
  64. bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
  65. /* Switch to CORE_BASE1E */
  66. phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
  67. r_rc_cal_reset(phydev);
  68. /* write AFE_RXCONFIG_0 */
  69. bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
  70. /* write AFE_RXCONFIG_1 */
  71. bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
  72. /* write AFE_RX_LP_COUNTER */
  73. bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
  74. /* write AFE_HPF_TRIM_OTHERS */
  75. bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
  76. /* write AFTE_TX_CONFIG */
  77. bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
  78. return 0;
  79. }
  80. static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev)
  81. {
  82. /* AFE_RXCONFIG_0 */
  83. bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb15);
  84. /* AFE_RXCONFIG_1 */
  85. bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
  86. /* AFE_RXCONFIG_2, set rCal offset for HT=0 code and LT=-2 code */
  87. bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0x2003);
  88. /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */
  89. bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
  90. /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
  91. bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
  92. /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
  93. bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
  94. /* AFE_VDAC_OTHERS_0, set 1000BT Cidac=010 for all ports */
  95. bcm_phy_write_misc(phydev, AFE_VDAC_OTHERS_0, 0xa020);
  96. /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
  97. * offset for HT=0 code
  98. */
  99. bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
  100. /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
  101. phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
  102. /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
  103. bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
  104. /* Reset R_CAL/RC_CAL engine */
  105. r_rc_cal_reset(phydev);
  106. return 0;
  107. }
  108. static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev)
  109. {
  110. /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */
  111. bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f);
  112. /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */
  113. bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x431);
  114. /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */
  115. bcm_phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da);
  116. /* AFE_HPF_TRIM_OTHERS, set 100Tx/10BT to -4.5% swing and set rCal
  117. * offset for HT=0 code
  118. */
  119. bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x00e3);
  120. /* CORE_BASE1E, force trim to overwrite and set I_ext trim to 0000 */
  121. phy_write(phydev, MII_BRCM_CORE_BASE1E, 0x0010);
  122. /* DSP_TAP10, adjust bias current trim (+0% swing, +0 tick) */
  123. bcm_phy_write_misc(phydev, DSP_TAP10, 0x011b);
  124. /* Reset R_CAL/RC_CAL engine */
  125. r_rc_cal_reset(phydev);
  126. return 0;
  127. }
  128. static int bcm7xxx_28nm_a0_patch_afe_config_init(struct phy_device *phydev)
  129. {
  130. /* +1 RC_CAL codes for RL centering for both LT and HT conditions */
  131. bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0xd003);
  132. /* Cut master bias current by 2% to compensate for RC_CAL offset */
  133. bcm_phy_write_misc(phydev, DSP_TAP10, 0x791b);
  134. /* Improve hybrid leakage */
  135. bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x10e3);
  136. /* Change rx_on_tune 8 to 0xf */
  137. bcm_phy_write_misc(phydev, 0x21, 0x2, 0x87f6);
  138. /* Change 100Tx EEE bandwidth */
  139. bcm_phy_write_misc(phydev, 0x22, 0x2, 0x017d);
  140. /* Enable ffe zero detection for Vitesse interoperability */
  141. bcm_phy_write_misc(phydev, 0x26, 0x2, 0x0015);
  142. r_rc_cal_reset(phydev);
  143. return 0;
  144. }
  145. static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
  146. {
  147. u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
  148. u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
  149. u8 count;
  150. int ret = 0;
  151. /* Newer devices have moved the revision information back into a
  152. * standard location in MII_PHYS_ID[23]
  153. */
  154. if (rev == 0)
  155. rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
  156. pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
  157. phydev_name(phydev), phydev->drv->name, rev, patch);
  158. /* Dummy read to a register to workaround an issue upon reset where the
  159. * internal inverter may not allow the first MDIO transaction to pass
  160. * the MDIO management controller and make us return 0xffff for such
  161. * reads.
  162. */
  163. phy_read(phydev, MII_BMSR);
  164. switch (rev) {
  165. case 0xb0:
  166. ret = bcm7xxx_28nm_b0_afe_config_init(phydev);
  167. break;
  168. case 0xd0:
  169. ret = bcm7xxx_28nm_d0_afe_config_init(phydev);
  170. break;
  171. case 0xe0:
  172. case 0xf0:
  173. /* Rev G0 introduces a roll over */
  174. case 0x10:
  175. ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
  176. break;
  177. case 0x01:
  178. ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev);
  179. break;
  180. default:
  181. break;
  182. }
  183. if (ret)
  184. return ret;
  185. ret = bcm_phy_downshift_get(phydev, &count);
  186. if (ret)
  187. return ret;
  188. /* Only enable EEE if Wirespeed/downshift is disabled */
  189. ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
  190. if (ret)
  191. return ret;
  192. return bcm_phy_enable_apd(phydev, true);
  193. }
  194. static int bcm7xxx_28nm_resume(struct phy_device *phydev)
  195. {
  196. int ret;
  197. /* Re-apply workarounds coming out suspend/resume */
  198. ret = bcm7xxx_28nm_config_init(phydev);
  199. if (ret)
  200. return ret;
  201. /* 28nm Gigabit PHYs come out of reset without any half-duplex
  202. * or "hub" compliant advertised mode, fix that. This does not
  203. * cause any problems with the PHY library since genphy_config_aneg()
  204. * gracefully handles auto-negotiated and forced modes.
  205. */
  206. return genphy_config_aneg(phydev);
  207. }
  208. static int phy_set_clr_bits(struct phy_device *dev, int location,
  209. int set_mask, int clr_mask)
  210. {
  211. int v, ret;
  212. v = phy_read(dev, location);
  213. if (v < 0)
  214. return v;
  215. v &= ~clr_mask;
  216. v |= set_mask;
  217. ret = phy_write(dev, location, v);
  218. if (ret < 0)
  219. return ret;
  220. return v;
  221. }
  222. static int bcm7xxx_config_init(struct phy_device *phydev)
  223. {
  224. int ret;
  225. /* Enable 64 clock MDIO */
  226. phy_write(phydev, MII_BCM7XXX_AUX_MODE, MII_BCM7XXX_64CLK_MDIO);
  227. phy_read(phydev, MII_BCM7XXX_AUX_MODE);
  228. /* set shadow mode 2 */
  229. ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST,
  230. MII_BCM7XXX_SHD_MODE_2, MII_BCM7XXX_SHD_MODE_2);
  231. if (ret < 0)
  232. return ret;
  233. /* set iddq_clkbias */
  234. phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0F00);
  235. udelay(10);
  236. /* reset iddq_clkbias */
  237. phy_write(phydev, MII_BCM7XXX_100TX_DISC, 0x0C00);
  238. phy_write(phydev, MII_BCM7XXX_100TX_FALSE_CAR, 0x7555);
  239. /* reset shadow mode 2 */
  240. ret = phy_set_clr_bits(phydev, MII_BCM7XXX_TEST, 0, MII_BCM7XXX_SHD_MODE_2);
  241. if (ret < 0)
  242. return ret;
  243. return 0;
  244. }
  245. /* Workaround for putting the PHY in IDDQ mode, required
  246. * for all BCM7XXX 40nm and 65nm PHYs
  247. */
  248. static int bcm7xxx_suspend(struct phy_device *phydev)
  249. {
  250. int ret;
  251. const struct bcm7xxx_regs {
  252. int reg;
  253. u16 value;
  254. } bcm7xxx_suspend_cfg[] = {
  255. { MII_BCM7XXX_TEST, 0x008b },
  256. { MII_BCM7XXX_100TX_AUX_CTL, 0x01c0 },
  257. { MII_BCM7XXX_100TX_DISC, 0x7000 },
  258. { MII_BCM7XXX_TEST, 0x000f },
  259. { MII_BCM7XXX_100TX_AUX_CTL, 0x20d0 },
  260. { MII_BCM7XXX_TEST, 0x000b },
  261. };
  262. unsigned int i;
  263. for (i = 0; i < ARRAY_SIZE(bcm7xxx_suspend_cfg); i++) {
  264. ret = phy_write(phydev,
  265. bcm7xxx_suspend_cfg[i].reg,
  266. bcm7xxx_suspend_cfg[i].value);
  267. if (ret)
  268. return ret;
  269. }
  270. return 0;
  271. }
  272. static int bcm7xxx_28nm_get_tunable(struct phy_device *phydev,
  273. struct ethtool_tunable *tuna,
  274. void *data)
  275. {
  276. switch (tuna->id) {
  277. case ETHTOOL_PHY_DOWNSHIFT:
  278. return bcm_phy_downshift_get(phydev, (u8 *)data);
  279. default:
  280. return -EOPNOTSUPP;
  281. }
  282. }
  283. static int bcm7xxx_28nm_set_tunable(struct phy_device *phydev,
  284. struct ethtool_tunable *tuna,
  285. const void *data)
  286. {
  287. u8 count = *(u8 *)data;
  288. int ret;
  289. switch (tuna->id) {
  290. case ETHTOOL_PHY_DOWNSHIFT:
  291. ret = bcm_phy_downshift_set(phydev, count);
  292. break;
  293. default:
  294. return -EOPNOTSUPP;
  295. }
  296. if (ret)
  297. return ret;
  298. /* Disable EEE advertisment since this prevents the PHY
  299. * from successfully linking up, trigger auto-negotiation restart
  300. * to let the MAC decide what to do.
  301. */
  302. ret = bcm_phy_set_eee(phydev, count == DOWNSHIFT_DEV_DISABLE);
  303. if (ret)
  304. return ret;
  305. return genphy_restart_aneg(phydev);
  306. }
  307. static void bcm7xxx_28nm_get_phy_stats(struct phy_device *phydev,
  308. struct ethtool_stats *stats, u64 *data)
  309. {
  310. struct bcm7xxx_phy_priv *priv = phydev->priv;
  311. bcm_phy_get_stats(phydev, priv->stats, stats, data);
  312. }
  313. static int bcm7xxx_28nm_probe(struct phy_device *phydev)
  314. {
  315. struct bcm7xxx_phy_priv *priv;
  316. priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
  317. if (!priv)
  318. return -ENOMEM;
  319. phydev->priv = priv;
  320. priv->stats = devm_kcalloc(&phydev->mdio.dev,
  321. bcm_phy_get_sset_count(phydev), sizeof(u64),
  322. GFP_KERNEL);
  323. if (!priv->stats)
  324. return -ENOMEM;
  325. return 0;
  326. }
  327. #define BCM7XXX_28NM_GPHY(_oui, _name) \
  328. { \
  329. .phy_id = (_oui), \
  330. .phy_id_mask = 0xfffffff0, \
  331. .name = _name, \
  332. .features = PHY_GBIT_FEATURES, \
  333. .flags = PHY_IS_INTERNAL, \
  334. .config_init = bcm7xxx_28nm_config_init, \
  335. .config_aneg = genphy_config_aneg, \
  336. .read_status = genphy_read_status, \
  337. .resume = bcm7xxx_28nm_resume, \
  338. .get_tunable = bcm7xxx_28nm_get_tunable, \
  339. .set_tunable = bcm7xxx_28nm_set_tunable, \
  340. .get_sset_count = bcm_phy_get_sset_count, \
  341. .get_strings = bcm_phy_get_strings, \
  342. .get_stats = bcm7xxx_28nm_get_phy_stats, \
  343. .probe = bcm7xxx_28nm_probe, \
  344. }
  345. #define BCM7XXX_40NM_EPHY(_oui, _name) \
  346. { \
  347. .phy_id = (_oui), \
  348. .phy_id_mask = 0xfffffff0, \
  349. .name = _name, \
  350. .features = PHY_BASIC_FEATURES, \
  351. .flags = PHY_IS_INTERNAL, \
  352. .config_init = bcm7xxx_config_init, \
  353. .config_aneg = genphy_config_aneg, \
  354. .read_status = genphy_read_status, \
  355. .suspend = bcm7xxx_suspend, \
  356. .resume = bcm7xxx_config_init, \
  357. }
  358. static struct phy_driver bcm7xxx_driver[] = {
  359. BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
  360. BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278"),
  361. BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
  362. BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
  363. BCM7XXX_28NM_GPHY(PHY_ID_BCM74371, "Broadcom BCM74371"),
  364. BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
  365. BCM7XXX_28NM_GPHY(PHY_ID_BCM7439_2, "Broadcom BCM7439 (2)"),
  366. BCM7XXX_28NM_GPHY(PHY_ID_BCM7445, "Broadcom BCM7445"),
  367. BCM7XXX_40NM_EPHY(PHY_ID_BCM7346, "Broadcom BCM7346"),
  368. BCM7XXX_40NM_EPHY(PHY_ID_BCM7362, "Broadcom BCM7362"),
  369. BCM7XXX_40NM_EPHY(PHY_ID_BCM7425, "Broadcom BCM7425"),
  370. BCM7XXX_40NM_EPHY(PHY_ID_BCM7429, "Broadcom BCM7429"),
  371. BCM7XXX_40NM_EPHY(PHY_ID_BCM7435, "Broadcom BCM7435"),
  372. };
  373. static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
  374. { PHY_ID_BCM7250, 0xfffffff0, },
  375. { PHY_ID_BCM7278, 0xfffffff0, },
  376. { PHY_ID_BCM7364, 0xfffffff0, },
  377. { PHY_ID_BCM7366, 0xfffffff0, },
  378. { PHY_ID_BCM7346, 0xfffffff0, },
  379. { PHY_ID_BCM7362, 0xfffffff0, },
  380. { PHY_ID_BCM7425, 0xfffffff0, },
  381. { PHY_ID_BCM7429, 0xfffffff0, },
  382. { PHY_ID_BCM74371, 0xfffffff0, },
  383. { PHY_ID_BCM7439, 0xfffffff0, },
  384. { PHY_ID_BCM7435, 0xfffffff0, },
  385. { PHY_ID_BCM7445, 0xfffffff0, },
  386. { }
  387. };
  388. module_phy_driver(bcm7xxx_driver);
  389. MODULE_DEVICE_TABLE(mdio, bcm7xxx_tbl);
  390. MODULE_DESCRIPTION("Broadcom BCM7xxx internal PHY driver");
  391. MODULE_LICENSE("GPL");
  392. MODULE_AUTHOR("Broadcom Corporation");