mscc.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. /*
  2. * Driver for Microsemi VSC85xx PHYs
  3. *
  4. * Author: Nagaraju Lakkaraju
  5. * License: Dual MIT/GPL
  6. * Copyright (c) 2016 Microsemi Corporation
  7. */
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <linux/mdio.h>
  11. #include <linux/mii.h>
  12. #include <linux/phy.h>
  13. #include <linux/of.h>
  14. #include <linux/netdevice.h>
  15. enum rgmii_rx_clock_delay {
  16. RGMII_RX_CLK_DELAY_0_2_NS = 0,
  17. RGMII_RX_CLK_DELAY_0_8_NS = 1,
  18. RGMII_RX_CLK_DELAY_1_1_NS = 2,
  19. RGMII_RX_CLK_DELAY_1_7_NS = 3,
  20. RGMII_RX_CLK_DELAY_2_0_NS = 4,
  21. RGMII_RX_CLK_DELAY_2_3_NS = 5,
  22. RGMII_RX_CLK_DELAY_2_6_NS = 6,
  23. RGMII_RX_CLK_DELAY_3_4_NS = 7
  24. };
  25. /* Microsemi VSC85xx PHY registers */
  26. /* IEEE 802. Std Registers */
  27. #define MSCC_PHY_BYPASS_CONTROL 18
  28. #define DISABLE_HP_AUTO_MDIX_MASK 0x0080
  29. #define DISABLE_PAIR_SWAP_CORR_MASK 0x0020
  30. #define DISABLE_POLARITY_CORR_MASK 0x0010
  31. #define MSCC_PHY_EXT_PHY_CNTL_1 23
  32. #define MAC_IF_SELECTION_MASK 0x1800
  33. #define MAC_IF_SELECTION_GMII 0
  34. #define MAC_IF_SELECTION_RMII 1
  35. #define MAC_IF_SELECTION_RGMII 2
  36. #define MAC_IF_SELECTION_POS 11
  37. #define FAR_END_LOOPBACK_MODE_MASK 0x0008
  38. #define MII_VSC85XX_INT_MASK 25
  39. #define MII_VSC85XX_INT_MASK_MASK 0xa000
  40. #define MII_VSC85XX_INT_MASK_WOL 0x0040
  41. #define MII_VSC85XX_INT_STATUS 26
  42. #define MSCC_PHY_WOL_MAC_CONTROL 27
  43. #define EDGE_RATE_CNTL_POS 5
  44. #define EDGE_RATE_CNTL_MASK 0x00E0
  45. #define MSCC_PHY_DEV_AUX_CNTL 28
  46. #define HP_AUTO_MDIX_X_OVER_IND_MASK 0x2000
  47. #define MSCC_EXT_PAGE_ACCESS 31
  48. #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
  49. #define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */
  50. #define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */
  51. /* Extended Page 1 Registers */
  52. #define MSCC_PHY_EXT_MODE_CNTL 19
  53. #define FORCE_MDI_CROSSOVER_MASK 0x000C
  54. #define FORCE_MDI_CROSSOVER_MDIX 0x000C
  55. #define FORCE_MDI_CROSSOVER_MDI 0x0008
  56. #define MSCC_PHY_ACTIPHY_CNTL 20
  57. #define DOWNSHIFT_CNTL_MASK 0x001C
  58. #define DOWNSHIFT_EN 0x0010
  59. #define DOWNSHIFT_CNTL_POS 2
  60. /* Extended Page 2 Registers */
  61. #define MSCC_PHY_RGMII_CNTL 20
  62. #define RGMII_RX_CLK_DELAY_MASK 0x0070
  63. #define RGMII_RX_CLK_DELAY_POS 4
  64. #define MSCC_PHY_WOL_LOWER_MAC_ADDR 21
  65. #define MSCC_PHY_WOL_MID_MAC_ADDR 22
  66. #define MSCC_PHY_WOL_UPPER_MAC_ADDR 23
  67. #define MSCC_PHY_WOL_LOWER_PASSWD 24
  68. #define MSCC_PHY_WOL_MID_PASSWD 25
  69. #define MSCC_PHY_WOL_UPPER_PASSWD 26
  70. #define MSCC_PHY_WOL_MAC_CONTROL 27
  71. #define SECURE_ON_ENABLE 0x8000
  72. #define SECURE_ON_PASSWD_LEN_4 0x4000
  73. /* Microsemi PHY ID's */
  74. #define PHY_ID_VSC8530 0x00070560
  75. #define PHY_ID_VSC8531 0x00070570
  76. #define PHY_ID_VSC8540 0x00070760
  77. #define PHY_ID_VSC8541 0x00070770
  78. #define MSCC_VDDMAC_1500 1500
  79. #define MSCC_VDDMAC_1800 1800
  80. #define MSCC_VDDMAC_2500 2500
  81. #define MSCC_VDDMAC_3300 3300
  82. #define DOWNSHIFT_COUNT_MAX 5
  83. struct vsc8531_private {
  84. int rate_magic;
  85. };
  86. #ifdef CONFIG_OF_MDIO
  87. struct vsc8531_edge_rate_table {
  88. u16 vddmac;
  89. u8 slowdown[8];
  90. };
  91. static const struct vsc8531_edge_rate_table edge_table[] = {
  92. {MSCC_VDDMAC_3300, { 0, 2, 4, 7, 10, 17, 29, 53} },
  93. {MSCC_VDDMAC_2500, { 0, 3, 6, 10, 14, 23, 37, 63} },
  94. {MSCC_VDDMAC_1800, { 0, 5, 9, 16, 23, 35, 52, 76} },
  95. {MSCC_VDDMAC_1500, { 0, 6, 14, 21, 29, 42, 58, 77} },
  96. };
  97. #endif /* CONFIG_OF_MDIO */
  98. static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page)
  99. {
  100. int rc;
  101. rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
  102. return rc;
  103. }
  104. static int vsc85xx_mdix_get(struct phy_device *phydev, u8 *mdix)
  105. {
  106. u16 reg_val;
  107. reg_val = phy_read(phydev, MSCC_PHY_DEV_AUX_CNTL);
  108. if (reg_val & HP_AUTO_MDIX_X_OVER_IND_MASK)
  109. *mdix = ETH_TP_MDI_X;
  110. else
  111. *mdix = ETH_TP_MDI;
  112. return 0;
  113. }
  114. static int vsc85xx_mdix_set(struct phy_device *phydev, u8 mdix)
  115. {
  116. int rc;
  117. u16 reg_val;
  118. reg_val = phy_read(phydev, MSCC_PHY_BYPASS_CONTROL);
  119. if ((mdix == ETH_TP_MDI) || (mdix == ETH_TP_MDI_X)) {
  120. reg_val |= (DISABLE_PAIR_SWAP_CORR_MASK |
  121. DISABLE_POLARITY_CORR_MASK |
  122. DISABLE_HP_AUTO_MDIX_MASK);
  123. } else {
  124. reg_val &= ~(DISABLE_PAIR_SWAP_CORR_MASK |
  125. DISABLE_POLARITY_CORR_MASK |
  126. DISABLE_HP_AUTO_MDIX_MASK);
  127. }
  128. rc = phy_write(phydev, MSCC_PHY_BYPASS_CONTROL, reg_val);
  129. if (rc != 0)
  130. return rc;
  131. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
  132. if (rc != 0)
  133. return rc;
  134. reg_val = phy_read(phydev, MSCC_PHY_EXT_MODE_CNTL);
  135. reg_val &= ~(FORCE_MDI_CROSSOVER_MASK);
  136. if (mdix == ETH_TP_MDI)
  137. reg_val |= FORCE_MDI_CROSSOVER_MDI;
  138. else if (mdix == ETH_TP_MDI_X)
  139. reg_val |= FORCE_MDI_CROSSOVER_MDIX;
  140. rc = phy_write(phydev, MSCC_PHY_EXT_MODE_CNTL, reg_val);
  141. if (rc != 0)
  142. return rc;
  143. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
  144. if (rc != 0)
  145. return rc;
  146. return genphy_restart_aneg(phydev);
  147. }
  148. static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count)
  149. {
  150. int rc;
  151. u16 reg_val;
  152. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
  153. if (rc != 0)
  154. goto out;
  155. reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
  156. reg_val &= DOWNSHIFT_CNTL_MASK;
  157. if (!(reg_val & DOWNSHIFT_EN))
  158. *count = DOWNSHIFT_DEV_DISABLE;
  159. else
  160. *count = ((reg_val & ~DOWNSHIFT_EN) >> DOWNSHIFT_CNTL_POS) + 2;
  161. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
  162. out:
  163. return rc;
  164. }
  165. static int vsc85xx_downshift_set(struct phy_device *phydev, u8 count)
  166. {
  167. int rc;
  168. u16 reg_val;
  169. if (count == DOWNSHIFT_DEV_DEFAULT_COUNT) {
  170. /* Default downshift count 3 (i.e. Bit3:2 = 0b01) */
  171. count = ((1 << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN);
  172. } else if (count > DOWNSHIFT_COUNT_MAX || count == 1) {
  173. phydev_err(phydev, "Downshift count should be 2,3,4 or 5\n");
  174. return -ERANGE;
  175. } else if (count) {
  176. /* Downshift count is either 2,3,4 or 5 */
  177. count = (((count - 2) << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN);
  178. }
  179. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
  180. if (rc != 0)
  181. goto out;
  182. reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
  183. reg_val &= ~(DOWNSHIFT_CNTL_MASK);
  184. reg_val |= count;
  185. rc = phy_write(phydev, MSCC_PHY_ACTIPHY_CNTL, reg_val);
  186. if (rc != 0)
  187. goto out;
  188. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
  189. out:
  190. return rc;
  191. }
  192. static int vsc85xx_wol_set(struct phy_device *phydev,
  193. struct ethtool_wolinfo *wol)
  194. {
  195. int rc;
  196. u16 reg_val;
  197. u8 i;
  198. u16 pwd[3] = {0, 0, 0};
  199. struct ethtool_wolinfo *wol_conf = wol;
  200. u8 *mac_addr = phydev->attached_dev->dev_addr;
  201. mutex_lock(&phydev->lock);
  202. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
  203. if (rc != 0)
  204. goto out_unlock;
  205. if (wol->wolopts & WAKE_MAGIC) {
  206. /* Store the device address for the magic packet */
  207. for (i = 0; i < ARRAY_SIZE(pwd); i++)
  208. pwd[i] = mac_addr[5 - (i * 2 + 1)] << 8 |
  209. mac_addr[5 - i * 2];
  210. phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, pwd[0]);
  211. phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, pwd[1]);
  212. phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, pwd[2]);
  213. } else {
  214. phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, 0);
  215. phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, 0);
  216. phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, 0);
  217. }
  218. if (wol_conf->wolopts & WAKE_MAGICSECURE) {
  219. for (i = 0; i < ARRAY_SIZE(pwd); i++)
  220. pwd[i] = wol_conf->sopass[5 - (i * 2 + 1)] << 8 |
  221. wol_conf->sopass[5 - i * 2];
  222. phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, pwd[0]);
  223. phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, pwd[1]);
  224. phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, pwd[2]);
  225. } else {
  226. phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, 0);
  227. phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, 0);
  228. phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, 0);
  229. }
  230. reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
  231. if (wol_conf->wolopts & WAKE_MAGICSECURE)
  232. reg_val |= SECURE_ON_ENABLE;
  233. else
  234. reg_val &= ~SECURE_ON_ENABLE;
  235. phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
  236. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
  237. if (rc != 0)
  238. goto out_unlock;
  239. if (wol->wolopts & WAKE_MAGIC) {
  240. /* Enable the WOL interrupt */
  241. reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
  242. reg_val |= MII_VSC85XX_INT_MASK_WOL;
  243. rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
  244. if (rc != 0)
  245. goto out_unlock;
  246. } else {
  247. /* Disable the WOL interrupt */
  248. reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
  249. reg_val &= (~MII_VSC85XX_INT_MASK_WOL);
  250. rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
  251. if (rc != 0)
  252. goto out_unlock;
  253. }
  254. /* Clear WOL iterrupt status */
  255. reg_val = phy_read(phydev, MII_VSC85XX_INT_STATUS);
  256. out_unlock:
  257. mutex_unlock(&phydev->lock);
  258. return rc;
  259. }
  260. static void vsc85xx_wol_get(struct phy_device *phydev,
  261. struct ethtool_wolinfo *wol)
  262. {
  263. int rc;
  264. u16 reg_val;
  265. u8 i;
  266. u16 pwd[3] = {0, 0, 0};
  267. struct ethtool_wolinfo *wol_conf = wol;
  268. mutex_lock(&phydev->lock);
  269. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
  270. if (rc != 0)
  271. goto out_unlock;
  272. reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
  273. if (reg_val & SECURE_ON_ENABLE)
  274. wol_conf->wolopts |= WAKE_MAGICSECURE;
  275. if (wol_conf->wolopts & WAKE_MAGICSECURE) {
  276. pwd[0] = phy_read(phydev, MSCC_PHY_WOL_LOWER_PASSWD);
  277. pwd[1] = phy_read(phydev, MSCC_PHY_WOL_MID_PASSWD);
  278. pwd[2] = phy_read(phydev, MSCC_PHY_WOL_UPPER_PASSWD);
  279. for (i = 0; i < ARRAY_SIZE(pwd); i++) {
  280. wol_conf->sopass[5 - i * 2] = pwd[i] & 0x00ff;
  281. wol_conf->sopass[5 - (i * 2 + 1)] = (pwd[i] & 0xff00)
  282. >> 8;
  283. }
  284. }
  285. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
  286. out_unlock:
  287. mutex_unlock(&phydev->lock);
  288. }
  289. #ifdef CONFIG_OF_MDIO
  290. static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
  291. {
  292. u8 sd;
  293. u16 vdd;
  294. int rc, i, j;
  295. struct device *dev = &phydev->mdio.dev;
  296. struct device_node *of_node = dev->of_node;
  297. u8 sd_array_size = ARRAY_SIZE(edge_table[0].slowdown);
  298. if (!of_node)
  299. return -ENODEV;
  300. rc = of_property_read_u16(of_node, "vsc8531,vddmac", &vdd);
  301. if (rc != 0)
  302. vdd = MSCC_VDDMAC_3300;
  303. rc = of_property_read_u8(of_node, "vsc8531,edge-slowdown", &sd);
  304. if (rc != 0)
  305. sd = 0;
  306. for (i = 0; i < ARRAY_SIZE(edge_table); i++)
  307. if (edge_table[i].vddmac == vdd)
  308. for (j = 0; j < sd_array_size; j++)
  309. if (edge_table[i].slowdown[j] == sd)
  310. return (sd_array_size - j - 1);
  311. return -EINVAL;
  312. }
  313. #else
  314. static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
  315. {
  316. return 0;
  317. }
  318. #endif /* CONFIG_OF_MDIO */
  319. static int vsc85xx_edge_rate_cntl_set(struct phy_device *phydev, u8 edge_rate)
  320. {
  321. int rc;
  322. u16 reg_val;
  323. mutex_lock(&phydev->lock);
  324. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
  325. if (rc != 0)
  326. goto out_unlock;
  327. reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
  328. reg_val &= ~(EDGE_RATE_CNTL_MASK);
  329. reg_val |= (edge_rate << EDGE_RATE_CNTL_POS);
  330. rc = phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
  331. if (rc != 0)
  332. goto out_unlock;
  333. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
  334. out_unlock:
  335. mutex_unlock(&phydev->lock);
  336. return rc;
  337. }
  338. static int vsc85xx_mac_if_set(struct phy_device *phydev,
  339. phy_interface_t interface)
  340. {
  341. int rc;
  342. u16 reg_val;
  343. mutex_lock(&phydev->lock);
  344. reg_val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1);
  345. reg_val &= ~(MAC_IF_SELECTION_MASK);
  346. switch (interface) {
  347. case PHY_INTERFACE_MODE_RGMII:
  348. reg_val |= (MAC_IF_SELECTION_RGMII << MAC_IF_SELECTION_POS);
  349. break;
  350. case PHY_INTERFACE_MODE_RMII:
  351. reg_val |= (MAC_IF_SELECTION_RMII << MAC_IF_SELECTION_POS);
  352. break;
  353. case PHY_INTERFACE_MODE_MII:
  354. case PHY_INTERFACE_MODE_GMII:
  355. reg_val |= (MAC_IF_SELECTION_GMII << MAC_IF_SELECTION_POS);
  356. break;
  357. default:
  358. rc = -EINVAL;
  359. goto out_unlock;
  360. }
  361. rc = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, reg_val);
  362. if (rc != 0)
  363. goto out_unlock;
  364. rc = genphy_soft_reset(phydev);
  365. out_unlock:
  366. mutex_unlock(&phydev->lock);
  367. return rc;
  368. }
  369. static int vsc85xx_default_config(struct phy_device *phydev)
  370. {
  371. int rc;
  372. u16 reg_val;
  373. phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
  374. mutex_lock(&phydev->lock);
  375. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
  376. if (rc != 0)
  377. goto out_unlock;
  378. reg_val = phy_read(phydev, MSCC_PHY_RGMII_CNTL);
  379. reg_val &= ~(RGMII_RX_CLK_DELAY_MASK);
  380. reg_val |= (RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS);
  381. phy_write(phydev, MSCC_PHY_RGMII_CNTL, reg_val);
  382. rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
  383. out_unlock:
  384. mutex_unlock(&phydev->lock);
  385. return rc;
  386. }
  387. static int vsc85xx_get_tunable(struct phy_device *phydev,
  388. struct ethtool_tunable *tuna, void *data)
  389. {
  390. switch (tuna->id) {
  391. case ETHTOOL_PHY_DOWNSHIFT:
  392. return vsc85xx_downshift_get(phydev, (u8 *)data);
  393. default:
  394. return -EINVAL;
  395. }
  396. }
  397. static int vsc85xx_set_tunable(struct phy_device *phydev,
  398. struct ethtool_tunable *tuna,
  399. const void *data)
  400. {
  401. switch (tuna->id) {
  402. case ETHTOOL_PHY_DOWNSHIFT:
  403. return vsc85xx_downshift_set(phydev, *(u8 *)data);
  404. default:
  405. return -EINVAL;
  406. }
  407. }
  408. static int vsc85xx_config_init(struct phy_device *phydev)
  409. {
  410. int rc;
  411. struct vsc8531_private *vsc8531 = phydev->priv;
  412. rc = vsc85xx_default_config(phydev);
  413. if (rc)
  414. return rc;
  415. rc = vsc85xx_mac_if_set(phydev, phydev->interface);
  416. if (rc)
  417. return rc;
  418. rc = vsc85xx_edge_rate_cntl_set(phydev, vsc8531->rate_magic);
  419. if (rc)
  420. return rc;
  421. rc = genphy_config_init(phydev);
  422. return rc;
  423. }
  424. static int vsc85xx_ack_interrupt(struct phy_device *phydev)
  425. {
  426. int rc = 0;
  427. if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
  428. rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
  429. return (rc < 0) ? rc : 0;
  430. }
  431. static int vsc85xx_config_intr(struct phy_device *phydev)
  432. {
  433. int rc;
  434. if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
  435. rc = phy_write(phydev, MII_VSC85XX_INT_MASK,
  436. MII_VSC85XX_INT_MASK_MASK);
  437. } else {
  438. rc = phy_write(phydev, MII_VSC85XX_INT_MASK, 0);
  439. if (rc < 0)
  440. return rc;
  441. rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
  442. }
  443. return rc;
  444. }
  445. static int vsc85xx_config_aneg(struct phy_device *phydev)
  446. {
  447. int rc;
  448. rc = vsc85xx_mdix_set(phydev, phydev->mdix_ctrl);
  449. if (rc < 0)
  450. return rc;
  451. return genphy_config_aneg(phydev);
  452. }
  453. static int vsc85xx_read_status(struct phy_device *phydev)
  454. {
  455. int rc;
  456. rc = vsc85xx_mdix_get(phydev, &phydev->mdix);
  457. if (rc < 0)
  458. return rc;
  459. return genphy_read_status(phydev);
  460. }
  461. static int vsc85xx_probe(struct phy_device *phydev)
  462. {
  463. int rate_magic;
  464. struct vsc8531_private *vsc8531;
  465. rate_magic = vsc85xx_edge_rate_magic_get(phydev);
  466. if (rate_magic < 0)
  467. return rate_magic;
  468. vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
  469. if (!vsc8531)
  470. return -ENOMEM;
  471. phydev->priv = vsc8531;
  472. vsc8531->rate_magic = rate_magic;
  473. return 0;
  474. }
  475. /* Microsemi VSC85xx PHYs */
  476. static struct phy_driver vsc85xx_driver[] = {
  477. {
  478. .phy_id = PHY_ID_VSC8530,
  479. .name = "Microsemi FE VSC8530",
  480. .phy_id_mask = 0xfffffff0,
  481. .features = PHY_BASIC_FEATURES,
  482. .flags = PHY_HAS_INTERRUPT,
  483. .soft_reset = &genphy_soft_reset,
  484. .config_init = &vsc85xx_config_init,
  485. .config_aneg = &vsc85xx_config_aneg,
  486. .aneg_done = &genphy_aneg_done,
  487. .read_status = &vsc85xx_read_status,
  488. .ack_interrupt = &vsc85xx_ack_interrupt,
  489. .config_intr = &vsc85xx_config_intr,
  490. .suspend = &genphy_suspend,
  491. .resume = &genphy_resume,
  492. .probe = &vsc85xx_probe,
  493. .set_wol = &vsc85xx_wol_set,
  494. .get_wol = &vsc85xx_wol_get,
  495. .get_tunable = &vsc85xx_get_tunable,
  496. .set_tunable = &vsc85xx_set_tunable,
  497. },
  498. {
  499. .phy_id = PHY_ID_VSC8531,
  500. .name = "Microsemi VSC8531",
  501. .phy_id_mask = 0xfffffff0,
  502. .features = PHY_GBIT_FEATURES,
  503. .flags = PHY_HAS_INTERRUPT,
  504. .soft_reset = &genphy_soft_reset,
  505. .config_init = &vsc85xx_config_init,
  506. .config_aneg = &vsc85xx_config_aneg,
  507. .aneg_done = &genphy_aneg_done,
  508. .read_status = &vsc85xx_read_status,
  509. .ack_interrupt = &vsc85xx_ack_interrupt,
  510. .config_intr = &vsc85xx_config_intr,
  511. .suspend = &genphy_suspend,
  512. .resume = &genphy_resume,
  513. .probe = &vsc85xx_probe,
  514. .set_wol = &vsc85xx_wol_set,
  515. .get_wol = &vsc85xx_wol_get,
  516. .get_tunable = &vsc85xx_get_tunable,
  517. .set_tunable = &vsc85xx_set_tunable,
  518. },
  519. {
  520. .phy_id = PHY_ID_VSC8540,
  521. .name = "Microsemi FE VSC8540 SyncE",
  522. .phy_id_mask = 0xfffffff0,
  523. .features = PHY_BASIC_FEATURES,
  524. .flags = PHY_HAS_INTERRUPT,
  525. .soft_reset = &genphy_soft_reset,
  526. .config_init = &vsc85xx_config_init,
  527. .config_aneg = &vsc85xx_config_aneg,
  528. .aneg_done = &genphy_aneg_done,
  529. .read_status = &vsc85xx_read_status,
  530. .ack_interrupt = &vsc85xx_ack_interrupt,
  531. .config_intr = &vsc85xx_config_intr,
  532. .suspend = &genphy_suspend,
  533. .resume = &genphy_resume,
  534. .probe = &vsc85xx_probe,
  535. .set_wol = &vsc85xx_wol_set,
  536. .get_wol = &vsc85xx_wol_get,
  537. .get_tunable = &vsc85xx_get_tunable,
  538. .set_tunable = &vsc85xx_set_tunable,
  539. },
  540. {
  541. .phy_id = PHY_ID_VSC8541,
  542. .name = "Microsemi VSC8541 SyncE",
  543. .phy_id_mask = 0xfffffff0,
  544. .features = PHY_GBIT_FEATURES,
  545. .flags = PHY_HAS_INTERRUPT,
  546. .soft_reset = &genphy_soft_reset,
  547. .config_init = &vsc85xx_config_init,
  548. .config_aneg = &vsc85xx_config_aneg,
  549. .aneg_done = &genphy_aneg_done,
  550. .read_status = &vsc85xx_read_status,
  551. .ack_interrupt = &vsc85xx_ack_interrupt,
  552. .config_intr = &vsc85xx_config_intr,
  553. .suspend = &genphy_suspend,
  554. .resume = &genphy_resume,
  555. .probe = &vsc85xx_probe,
  556. .set_wol = &vsc85xx_wol_set,
  557. .get_wol = &vsc85xx_wol_get,
  558. .get_tunable = &vsc85xx_get_tunable,
  559. .set_tunable = &vsc85xx_set_tunable,
  560. }
  561. };
  562. module_phy_driver(vsc85xx_driver);
  563. static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
  564. { PHY_ID_VSC8530, 0xfffffff0, },
  565. { PHY_ID_VSC8531, 0xfffffff0, },
  566. { PHY_ID_VSC8540, 0xfffffff0, },
  567. { PHY_ID_VSC8541, 0xfffffff0, },
  568. { }
  569. };
  570. MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
  571. MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
  572. MODULE_AUTHOR("Nagaraju Lakkaraju");
  573. MODULE_LICENSE("Dual MIT/GPL");