mxl-86110.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * PHY driver for Maxlinear MXL86110
  4. *
  5. * Copyright 2023 MaxLinear Inc.
  6. *
  7. */
  8. #include <linux/bitfield.h>
  9. #include <linux/etherdevice.h>
  10. #include <linux/kernel.h>
  11. #include <linux/module.h>
  12. #include <linux/of.h>
  13. #include <linux/phy.h>
  14. /* PHY ID */
  15. #define PHY_ID_MXL86110 0xc1335580
  16. #define PHY_ID_MXL86111 0xc1335588
  17. /* required to access extended registers */
  18. #define MXL86110_EXTD_REG_ADDR_OFFSET 0x1E
  19. #define MXL86110_EXTD_REG_ADDR_DATA 0x1F
  20. #define PHY_IRQ_ENABLE_REG 0x12
  21. #define PHY_IRQ_ENABLE_REG_WOL BIT(6)
  22. /* different pages for EXTD access for MXL86111 */
  23. /* SerDes/PHY Control Access Register - COM_EXT_SMI_SDS_PHY */
  24. #define MXL86111_EXT_SMI_SDS_PHY_REG 0xA000
  25. #define MXL86111_EXT_SMI_SDS_PHYSPACE_MASK BIT(1)
  26. #define MXL86111_EXT_SMI_SDS_PHYFIBER_SPACE (0x1 << 1)
  27. #define MXL86111_EXT_SMI_SDS_PHYUTP_SPACE (0x0 << 1)
  28. #define MXL86111_EXT_SMI_SDS_PHY_AUTO 0xff
  29. /* SyncE Configuration Register - COM_EXT_SYNCE_CFG */
  30. #define MXL86110_EXT_SYNCE_CFG_REG 0xA012
  31. #define MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL BIT(4)
  32. #define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E_DURING_LNKDN BIT(5)
  33. #define MXL86110_EXT_SYNCE_CFG_EN_SYNC_E BIT(6)
  34. #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK GENMASK(3, 1)
  35. #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_125M_PLL 0
  36. #define MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M 4
  37. /* MAC Address registers */
  38. #define MXL86110_EXT_MAC_ADDR_CFG1 0xA007
  39. #define MXL86110_EXT_MAC_ADDR_CFG2 0xA008
  40. #define MXL86110_EXT_MAC_ADDR_CFG3 0xA009
  41. #define MXL86110_EXT_WOL_CFG_REG 0xA00A
  42. #define MXL86110_WOL_CFG_WOL_MASK BIT(3)
  43. /* RGMII register */
  44. #define MXL86110_EXT_RGMII_CFG1_REG 0xA003
  45. /* delay can be adjusted in steps of about 150ps */
  46. #define MXL86110_EXT_RGMII_CFG1_RX_NO_DELAY (0x0 << 10)
  47. /* Closest value to 2000 ps */
  48. #define MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS (0xD << 10)
  49. #define MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK GENMASK(13, 10)
  50. #define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS (0xD << 0)
  51. #define MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK GENMASK(3, 0)
  52. #define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS (0xD << 4)
  53. #define MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK GENMASK(7, 4)
  54. #define MXL86110_EXT_RGMII_CFG1_FULL_MASK \
  55. ((MXL86110_EXT_RGMII_CFG1_RX_DELAY_MASK) | \
  56. (MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_MASK) | \
  57. (MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_MASK))
  58. /* EXT Sleep Control register */
  59. #define MXL86110_UTP_EXT_SLEEP_CTRL_REG 0x27
  60. #define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_OFF 0
  61. #define MXL86110_UTP_EXT_SLEEP_CTRL_EN_SLEEP_SW_MASK BIT(15)
  62. /* RGMII In-Band Status and MDIO Configuration Register */
  63. #define MXL86110_EXT_RGMII_MDIO_CFG 0xA005
  64. #define MXL86110_RGMII_MDIO_CFG_EPA0_MASK GENMASK(6, 6)
  65. #define MXL86110_EXT_RGMII_MDIO_CFG_EBA_MASK GENMASK(5, 5)
  66. #define MXL86110_EXT_RGMII_MDIO_CFG_BA_MASK GENMASK(4, 0)
  67. #define MXL86110_MAX_LEDS 3
  68. /* LED registers and defines */
  69. #define MXL86110_COM_EXT_LED_GEN_CFG 0xA00B
  70. # define MXL86110_COM_EXT_LED_GEN_CFG_LFM(x) ((BIT(0) | BIT(1)) << (3 * (x)))
  71. # define MXL86110_COM_EXT_LED_GEN_CFG_LFME(x) (BIT(0) << (3 * (x)))
  72. # define MXL86110_COM_EXT_LED_GEN_CFG_LFE(x) (BIT(2) << (3 * (x)))
  73. #define MXL86110_LED0_CFG_REG 0xA00C
  74. #define MXL86110_LED1_CFG_REG 0xA00D
  75. #define MXL86110_LED2_CFG_REG 0xA00E
  76. #define MXL86110_LEDX_CFG_BLINK BIT(13)
  77. #define MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON BIT(12)
  78. #define MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON BIT(11)
  79. #define MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON BIT(10)
  80. #define MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON BIT(9)
  81. #define MXL86110_LEDX_CFG_LINK_UP_TX_ON BIT(8)
  82. #define MXL86110_LEDX_CFG_LINK_UP_RX_ON BIT(7)
  83. #define MXL86110_LEDX_CFG_LINK_UP_1GB_ON BIT(6)
  84. #define MXL86110_LEDX_CFG_LINK_UP_100MB_ON BIT(5)
  85. #define MXL86110_LEDX_CFG_LINK_UP_10MB_ON BIT(4)
  86. #define MXL86110_LEDX_CFG_LINK_UP_COLLISION BIT(3)
  87. #define MXL86110_LEDX_CFG_LINK_UP_1GB_BLINK BIT(2)
  88. #define MXL86110_LEDX_CFG_LINK_UP_100MB_BLINK BIT(1)
  89. #define MXL86110_LEDX_CFG_LINK_UP_10MB_BLINK BIT(0)
  90. #define MXL86110_LED_BLINK_CFG_REG 0xA00F
  91. #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_2HZ 0
  92. #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_4HZ BIT(0)
  93. #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_8HZ BIT(1)
  94. #define MXL86110_LED_BLINK_CFG_FREQ_MODE1_16HZ (BIT(1) | BIT(0))
  95. #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_2HZ 0
  96. #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_4HZ BIT(2)
  97. #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_8HZ BIT(3)
  98. #define MXL86110_LED_BLINK_CFG_FREQ_MODE2_16HZ (BIT(3) | BIT(2))
  99. #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_ON 0
  100. #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_67_ON (BIT(4))
  101. #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_75_ON (BIT(5))
  102. #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_83_ON (BIT(5) | BIT(4))
  103. #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_50_OFF (BIT(6))
  104. #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_33_ON (BIT(6) | BIT(4))
  105. #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_25_ON (BIT(6) | BIT(5))
  106. #define MXL86110_LED_BLINK_CFG_DUTY_CYCLE_17_ON (BIT(6) | BIT(5) | BIT(4))
  107. /* Chip Configuration Register - COM_EXT_CHIP_CFG */
  108. #define MXL86110_EXT_CHIP_CFG_REG 0xA001
  109. #define MXL86111_EXT_CHIP_CFG_MODE_SEL_MASK GENMASK(2, 0)
  110. #define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_RGMII 0
  111. #define MXL86111_EXT_CHIP_CFG_MODE_FIBER_TO_RGMII 1
  112. #define MXL86111_EXT_CHIP_CFG_MODE_UTP_FIBER_TO_RGMII 2
  113. #define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_SGMII 3
  114. #define MXL86111_EXT_CHIP_CFG_MODE_SGPHY_TO_RGMAC 4
  115. #define MXL86111_EXT_CHIP_CFG_MODE_SGMAC_TO_RGPHY 5
  116. #define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_FIBER_AUTO 6
  117. #define MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_FIBER_FORCE 7
  118. #define MXL86111_EXT_CHIP_CFG_CLDO_MASK GENMASK(5, 4)
  119. #define MXL86111_EXT_CHIP_CFG_CLDO_3V3 0
  120. #define MXL86111_EXT_CHIP_CFG_CLDO_2V5 1
  121. #define MXL86111_EXT_CHIP_CFG_CLDO_1V8_2 2
  122. #define MXL86111_EXT_CHIP_CFG_CLDO_1V8_3 3
  123. #define MXL86111_EXT_CHIP_CFG_CLDO_SHIFT 4
  124. #define MXL86111_EXT_CHIP_CFG_ELDO BIT(6)
  125. #define MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE BIT(8)
  126. #define MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE BIT(15)
  127. /* Specific Status Register - PHY_STAT */
  128. #define MXL86111_PHY_STAT_REG 0x11
  129. #define MXL86111_PHY_STAT_SPEED_MASK GENMASK(15, 14)
  130. #define MXL86111_PHY_STAT_SPEED_OFFSET 14
  131. #define MXL86111_PHY_STAT_SPEED_10M 0x0
  132. #define MXL86111_PHY_STAT_SPEED_100M 0x1
  133. #define MXL86111_PHY_STAT_SPEED_1000M 0x2
  134. #define MXL86111_PHY_STAT_DPX_OFFSET 13
  135. #define MXL86111_PHY_STAT_DPX BIT(13)
  136. #define MXL86111_PHY_STAT_LSRT BIT(10)
  137. /* 3 phy reg page modes,auto mode combines utp and fiber mode*/
  138. #define MXL86111_MODE_FIBER 0x1
  139. #define MXL86111_MODE_UTP 0x2
  140. #define MXL86111_MODE_AUTO 0x3
  141. /* FIBER Auto-Negotiation link partner ability - SDS_AN_LPA */
  142. #define MXL86111_SDS_AN_LPA_PAUSE (0x3 << 7)
  143. #define MXL86111_SDS_AN_LPA_ASYM_PAUSE (0x2 << 7)
  144. /* Miscellaneous Control Register - COM_EXT _MISC_CFG */
  145. #define MXL86111_EXT_MISC_CONFIG_REG 0xa006
  146. #define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL BIT(0)
  147. #define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_1000BX (0x1 << 0)
  148. #define MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_100BX (0x0 << 0)
  149. /* Phy fiber Link timer cfg2 Register - EXT_SDS_LINK_TIMER_CFG2 */
  150. #define MXL86111_EXT_SDS_LINK_TIMER_CFG2_REG 0xA5
  151. #define MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN BIT(15)
  152. /* default values of PHY register, required for Dual Media mode */
  153. #define MII_BMSR_DEFAULT_VAL 0x7949
  154. #define MII_ESTATUS_DEFAULT_VAL 0x2000
  155. /* Timeout in ms for PHY SW reset check in STD_CTRL/SDS_CTRL */
  156. #define BMCR_RESET_TIMEOUT 500
  157. /* PL P1 requires optimized RGMII timing for 1.8V RGMII voltage
  158. */
  159. #define MXL86111_PL_P1 0x500
  160. /**
  161. * __mxl86110_write_extended_reg() - write to a PHY's extended register
  162. * @phydev: pointer to the PHY device structure
  163. * @regnum: register number to write
  164. * @val: value to write to @regnum
  165. *
  166. * Unlocked version of mxl86110_write_extended_reg
  167. *
  168. * Note: This function assumes the caller already holds the MDIO bus lock
  169. * or otherwise has exclusive access to the PHY.
  170. *
  171. * Return: 0 or negative error code
  172. */
  173. static int __mxl86110_write_extended_reg(struct phy_device *phydev,
  174. u16 regnum, u16 val)
  175. {
  176. int ret;
  177. ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
  178. if (ret < 0)
  179. return ret;
  180. return __phy_write(phydev, MXL86110_EXTD_REG_ADDR_DATA, val);
  181. }
  182. /**
  183. * __mxl86110_read_extended_reg - Read a PHY's extended register
  184. * @phydev: pointer to the PHY device structure
  185. * @regnum: extended register number to read (address written to reg 30)
  186. *
  187. * Unlocked version of mxl86110_read_extended_reg
  188. *
  189. * Reads the content of a PHY extended register using the MaxLinear
  190. * 2-step access mechanism: write the register address to reg 30 (0x1E),
  191. * then read the value from reg 31 (0x1F).
  192. *
  193. * Note: This function assumes the caller already holds the MDIO bus lock
  194. * or otherwise has exclusive access to the PHY.
  195. *
  196. * Return: 16-bit register value on success, or negative errno code on failure.
  197. */
  198. static int __mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum)
  199. {
  200. int ret;
  201. ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
  202. if (ret < 0)
  203. return ret;
  204. return __phy_read(phydev, MXL86110_EXTD_REG_ADDR_DATA);
  205. }
  206. /**
  207. * __mxl86110_modify_extended_reg() - modify bits of a PHY's extended register
  208. * @phydev: pointer to the PHY device structure
  209. * @regnum: register number to write
  210. * @mask: bit mask of bits to clear
  211. * @set: bit mask of bits to set
  212. *
  213. * Note: register value = (old register value & ~mask) | set.
  214. * This function assumes the caller already holds the MDIO bus lock
  215. * or otherwise has exclusive access to the PHY.
  216. *
  217. * Return: 0 or negative error code
  218. */
  219. static int __mxl86110_modify_extended_reg(struct phy_device *phydev,
  220. u16 regnum, u16 mask, u16 set)
  221. {
  222. int ret;
  223. ret = __phy_write(phydev, MXL86110_EXTD_REG_ADDR_OFFSET, regnum);
  224. if (ret < 0)
  225. return ret;
  226. return __phy_modify(phydev, MXL86110_EXTD_REG_ADDR_DATA, mask, set);
  227. }
  228. /**
  229. * mxl86110_write_extended_reg() - Write to a PHY's extended register
  230. * @phydev: pointer to the PHY device structure
  231. * @regnum: register number to write
  232. * @val: value to write to @regnum
  233. *
  234. * This function writes to an extended register of the PHY using the
  235. * MaxLinear two-step access method (reg 0x1E/0x1F). It handles acquiring
  236. * and releasing the MDIO bus lock internally.
  237. *
  238. * Return: 0 or negative error code
  239. */
  240. static int mxl86110_write_extended_reg(struct phy_device *phydev,
  241. u16 regnum, u16 val)
  242. {
  243. int ret;
  244. phy_lock_mdio_bus(phydev);
  245. ret = __mxl86110_write_extended_reg(phydev, regnum, val);
  246. phy_unlock_mdio_bus(phydev);
  247. return ret;
  248. }
  249. /**
  250. * mxl86110_read_extended_reg() - Read a PHY's extended register
  251. * @phydev: pointer to the PHY device structure
  252. * @regnum: extended register number to read
  253. *
  254. * This function reads from an extended register of the PHY using the
  255. * MaxLinear two-step access method (reg 0x1E/0x1F). It handles acquiring
  256. * and releasing the MDIO bus lock internally.
  257. *
  258. * Return: 16-bit register value on success, or negative errno code on failure
  259. */
  260. static int mxl86110_read_extended_reg(struct phy_device *phydev, u16 regnum)
  261. {
  262. int ret;
  263. phy_lock_mdio_bus(phydev);
  264. ret = __mxl86110_read_extended_reg(phydev, regnum);
  265. phy_unlock_mdio_bus(phydev);
  266. return ret;
  267. }
  268. /**
  269. * mxl86110_modify_extended_reg() - modify bits of a PHY's extended register
  270. * @phydev: pointer to the PHY device structure
  271. * @regnum: register number to write
  272. * @mask: bit mask of bits to clear
  273. * @set: bit mask of bits to set
  274. *
  275. * Note: register value = (old register value & ~mask) | set.
  276. *
  277. * Return: 0 or negative error code
  278. */
  279. static int mxl86110_modify_extended_reg(struct phy_device *phydev,
  280. u16 regnum, u16 mask, u16 set)
  281. {
  282. int ret;
  283. phy_lock_mdio_bus(phydev);
  284. ret = __mxl86110_modify_extended_reg(phydev, regnum, mask, set);
  285. phy_unlock_mdio_bus(phydev);
  286. return ret;
  287. }
  288. /**
  289. * mxl86110_get_wol() - report if wake-on-lan is enabled
  290. * @phydev: pointer to the phy_device
  291. * @wol: a pointer to a &struct ethtool_wolinfo
  292. */
  293. static void mxl86110_get_wol(struct phy_device *phydev,
  294. struct ethtool_wolinfo *wol)
  295. {
  296. int val;
  297. wol->supported = WAKE_MAGIC;
  298. wol->wolopts = 0;
  299. val = mxl86110_read_extended_reg(phydev, MXL86110_EXT_WOL_CFG_REG);
  300. if (val >= 0 && (val & MXL86110_WOL_CFG_WOL_MASK))
  301. wol->wolopts |= WAKE_MAGIC;
  302. }
  303. /**
  304. * mxl86110_set_wol() - enable/disable wake-on-lan
  305. * @phydev: pointer to the phy_device
  306. * @wol: a pointer to a &struct ethtool_wolinfo
  307. *
  308. * Configures the WOL Magic Packet MAC
  309. *
  310. * Return: 0 or negative errno code
  311. */
  312. static int mxl86110_set_wol(struct phy_device *phydev,
  313. struct ethtool_wolinfo *wol)
  314. {
  315. struct net_device *netdev;
  316. const unsigned char *mac;
  317. int ret = 0;
  318. phy_lock_mdio_bus(phydev);
  319. if (wol->wolopts & WAKE_MAGIC) {
  320. netdev = phydev->attached_dev;
  321. if (!netdev) {
  322. ret = -ENODEV;
  323. goto out;
  324. }
  325. /* Configure the MAC address of the WOL magic packet */
  326. mac = netdev->dev_addr;
  327. ret = __mxl86110_write_extended_reg(phydev,
  328. MXL86110_EXT_MAC_ADDR_CFG1,
  329. ((mac[0] << 8) | mac[1]));
  330. if (ret < 0)
  331. goto out;
  332. ret = __mxl86110_write_extended_reg(phydev,
  333. MXL86110_EXT_MAC_ADDR_CFG2,
  334. ((mac[2] << 8) | mac[3]));
  335. if (ret < 0)
  336. goto out;
  337. ret = __mxl86110_write_extended_reg(phydev,
  338. MXL86110_EXT_MAC_ADDR_CFG3,
  339. ((mac[4] << 8) | mac[5]));
  340. if (ret < 0)
  341. goto out;
  342. ret = __mxl86110_modify_extended_reg(phydev,
  343. MXL86110_EXT_WOL_CFG_REG,
  344. MXL86110_WOL_CFG_WOL_MASK,
  345. MXL86110_WOL_CFG_WOL_MASK);
  346. if (ret < 0)
  347. goto out;
  348. /* Enables Wake-on-LAN interrupt in the PHY. */
  349. ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG, 0,
  350. PHY_IRQ_ENABLE_REG_WOL);
  351. if (ret < 0)
  352. goto out;
  353. phydev_dbg(phydev,
  354. "%s, MAC Addr: %02X:%02X:%02X:%02X:%02X:%02X\n",
  355. __func__,
  356. mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  357. } else {
  358. ret = __mxl86110_modify_extended_reg(phydev,
  359. MXL86110_EXT_WOL_CFG_REG,
  360. MXL86110_WOL_CFG_WOL_MASK,
  361. 0);
  362. if (ret < 0)
  363. goto out;
  364. /* Disables Wake-on-LAN interrupt in the PHY. */
  365. ret = __phy_modify(phydev, PHY_IRQ_ENABLE_REG,
  366. PHY_IRQ_ENABLE_REG_WOL, 0);
  367. }
  368. out:
  369. phy_unlock_mdio_bus(phydev);
  370. return ret;
  371. }
  372. static const unsigned long supported_trgs = (BIT(TRIGGER_NETDEV_LINK_10) |
  373. BIT(TRIGGER_NETDEV_LINK_100) |
  374. BIT(TRIGGER_NETDEV_LINK_1000) |
  375. BIT(TRIGGER_NETDEV_HALF_DUPLEX) |
  376. BIT(TRIGGER_NETDEV_FULL_DUPLEX) |
  377. BIT(TRIGGER_NETDEV_TX) |
  378. BIT(TRIGGER_NETDEV_RX));
  379. static int mxl86110_led_hw_is_supported(struct phy_device *phydev, u8 index,
  380. unsigned long rules)
  381. {
  382. if (index >= MXL86110_MAX_LEDS)
  383. return -EINVAL;
  384. /* All combinations of the supported triggers are allowed */
  385. if (rules & ~supported_trgs)
  386. return -EOPNOTSUPP;
  387. return 0;
  388. }
  389. static int mxl86110_led_hw_control_get(struct phy_device *phydev, u8 index,
  390. unsigned long *rules)
  391. {
  392. int val;
  393. if (index >= MXL86110_MAX_LEDS)
  394. return -EINVAL;
  395. val = mxl86110_read_extended_reg(phydev,
  396. MXL86110_LED0_CFG_REG + index);
  397. if (val < 0)
  398. return val;
  399. if (val & MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON)
  400. *rules |= BIT(TRIGGER_NETDEV_TX);
  401. if (val & MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON)
  402. *rules |= BIT(TRIGGER_NETDEV_RX);
  403. if (val & MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON)
  404. *rules |= BIT(TRIGGER_NETDEV_HALF_DUPLEX);
  405. if (val & MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON)
  406. *rules |= BIT(TRIGGER_NETDEV_FULL_DUPLEX);
  407. if (val & MXL86110_LEDX_CFG_LINK_UP_10MB_ON)
  408. *rules |= BIT(TRIGGER_NETDEV_LINK_10);
  409. if (val & MXL86110_LEDX_CFG_LINK_UP_100MB_ON)
  410. *rules |= BIT(TRIGGER_NETDEV_LINK_100);
  411. if (val & MXL86110_LEDX_CFG_LINK_UP_1GB_ON)
  412. *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
  413. return 0;
  414. }
  415. static int mxl86110_led_hw_control_set(struct phy_device *phydev, u8 index,
  416. unsigned long rules)
  417. {
  418. u16 val = 0;
  419. int ret;
  420. if (index >= MXL86110_MAX_LEDS)
  421. return -EINVAL;
  422. if (rules & BIT(TRIGGER_NETDEV_LINK_10))
  423. val |= MXL86110_LEDX_CFG_LINK_UP_10MB_ON;
  424. if (rules & BIT(TRIGGER_NETDEV_LINK_100))
  425. val |= MXL86110_LEDX_CFG_LINK_UP_100MB_ON;
  426. if (rules & BIT(TRIGGER_NETDEV_LINK_1000))
  427. val |= MXL86110_LEDX_CFG_LINK_UP_1GB_ON;
  428. if (rules & BIT(TRIGGER_NETDEV_TX))
  429. val |= MXL86110_LEDX_CFG_LINK_UP_TX_ACT_ON;
  430. if (rules & BIT(TRIGGER_NETDEV_RX))
  431. val |= MXL86110_LEDX_CFG_LINK_UP_RX_ACT_ON;
  432. if (rules & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
  433. val |= MXL86110_LEDX_CFG_LINK_UP_HALF_DUPLEX_ON;
  434. if (rules & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
  435. val |= MXL86110_LEDX_CFG_LINK_UP_FULL_DUPLEX_ON;
  436. if (rules & BIT(TRIGGER_NETDEV_TX) ||
  437. rules & BIT(TRIGGER_NETDEV_RX))
  438. val |= MXL86110_LEDX_CFG_BLINK;
  439. ret = mxl86110_write_extended_reg(phydev,
  440. MXL86110_LED0_CFG_REG + index, val);
  441. if (ret)
  442. return ret;
  443. /* clear manual control bit */
  444. ret = mxl86110_modify_extended_reg(phydev,
  445. MXL86110_COM_EXT_LED_GEN_CFG,
  446. MXL86110_COM_EXT_LED_GEN_CFG_LFE(index),
  447. 0);
  448. return ret;
  449. }
  450. static int mxl86110_led_brightness_set(struct phy_device *phydev,
  451. u8 index, enum led_brightness value)
  452. {
  453. u16 mask, set;
  454. int ret;
  455. if (index >= MXL86110_MAX_LEDS)
  456. return -EINVAL;
  457. /* force manual control */
  458. set = MXL86110_COM_EXT_LED_GEN_CFG_LFE(index);
  459. /* clear previous force mode */
  460. mask = MXL86110_COM_EXT_LED_GEN_CFG_LFM(index);
  461. /* force LED to be permanently on */
  462. if (value != LED_OFF)
  463. set |= MXL86110_COM_EXT_LED_GEN_CFG_LFME(index);
  464. ret = mxl86110_modify_extended_reg(phydev,
  465. MXL86110_COM_EXT_LED_GEN_CFG,
  466. mask, set);
  467. return ret;
  468. }
  469. /**
  470. * mxl86110_synce_clk_cfg() - applies syncE/clk output configuration
  471. * @phydev: pointer to the phy_device
  472. *
  473. * Note: This function assumes the caller already holds the MDIO bus lock
  474. * or otherwise has exclusive access to the PHY.
  475. *
  476. * Return: 0 or negative errno code
  477. */
  478. static int mxl86110_synce_clk_cfg(struct phy_device *phydev)
  479. {
  480. u16 mask = 0, val = 0;
  481. /*
  482. * Configures the clock output to its default
  483. * setting as per the datasheet.
  484. * This results in a 25MHz clock output being selected in the
  485. * COM_EXT_SYNCE_CFG register for SyncE configuration.
  486. */
  487. val = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E |
  488. FIELD_PREP(MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK,
  489. MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_25M);
  490. mask = MXL86110_EXT_SYNCE_CFG_EN_SYNC_E |
  491. MXL86110_EXT_SYNCE_CFG_CLK_SRC_SEL_MASK |
  492. MXL86110_EXT_SYNCE_CFG_CLK_FRE_SEL;
  493. /* Write clock output configuration */
  494. return __mxl86110_modify_extended_reg(phydev,
  495. MXL86110_EXT_SYNCE_CFG_REG,
  496. mask, val);
  497. }
  498. /**
  499. * mxl86110_broadcast_cfg - Configure MDIO broadcast setting for PHY
  500. * @phydev: Pointer to the PHY device structure
  501. *
  502. * This function configures the MDIO broadcast behavior of the MxL86110 PHY.
  503. * Currently, broadcast mode is explicitly disabled by clearing the EPA0 bit
  504. * in the RGMII_MDIO_CFG extended register.
  505. *
  506. * Note: This function assumes the caller already holds the MDIO bus lock
  507. * or otherwise has exclusive access to the PHY.
  508. *
  509. * Return: 0 on success or a negative errno code on failure.
  510. */
  511. static int mxl86110_broadcast_cfg(struct phy_device *phydev)
  512. {
  513. return __mxl86110_modify_extended_reg(phydev,
  514. MXL86110_EXT_RGMII_MDIO_CFG,
  515. MXL86110_RGMII_MDIO_CFG_EPA0_MASK,
  516. 0);
  517. }
  518. /**
  519. * mxl86110_enable_led_activity_blink - Enable LEDs activity blink on PHY
  520. * @phydev: Pointer to the PHY device structure
  521. *
  522. * Configure all PHY LEDs to blink on traffic activity regardless of whether
  523. * they are ON or OFF. This behavior allows each LED to serve as a pure activity
  524. * indicator, independently of its use as a link status indicator.
  525. *
  526. * By default, each LED blinks only when it is also in the ON state.
  527. * This function modifies the appropriate registers (LABx fields)
  528. * to enable blinking even when the LEDs are OFF, to allow the LED to be used
  529. * as a traffic indicator without requiring it to also serve
  530. * as a link status LED.
  531. *
  532. * Note: Any further LED customization can be performed via the
  533. * /sys/class/leds interface; the functions led_hw_is_supported,
  534. * led_hw_control_get, and led_hw_control_set are used
  535. * to support this mechanism.
  536. *
  537. * This function assumes the caller already holds the MDIO bus lock
  538. * or otherwise has exclusive access to the PHY.
  539. *
  540. * Return: 0 on success or a negative errno code on failure.
  541. */
  542. static int mxl86110_enable_led_activity_blink(struct phy_device *phydev)
  543. {
  544. int i, ret = 0;
  545. for (i = 0; i < MXL86110_MAX_LEDS; i++) {
  546. ret = __mxl86110_modify_extended_reg(phydev,
  547. MXL86110_LED0_CFG_REG + i,
  548. 0,
  549. MXL86110_LEDX_CFG_BLINK);
  550. if (ret < 0)
  551. break;
  552. }
  553. return ret;
  554. }
  555. /**
  556. * mxl86110_config_rgmii_delay() - configure RGMII delays
  557. * @phydev: pointer to the phy_device
  558. *
  559. * Return: 0 or negative errno code
  560. */
  561. static int mxl86110_config_rgmii_delay(struct phy_device *phydev)
  562. {
  563. int ret;
  564. u16 val;
  565. switch (phydev->interface) {
  566. case PHY_INTERFACE_MODE_RGMII:
  567. val = 0;
  568. break;
  569. case PHY_INTERFACE_MODE_RGMII_RXID:
  570. val = MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS;
  571. break;
  572. case PHY_INTERFACE_MODE_RGMII_TXID:
  573. val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS |
  574. MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS;
  575. break;
  576. case PHY_INTERFACE_MODE_RGMII_ID:
  577. val = MXL86110_EXT_RGMII_CFG1_TX_1G_DELAY_1950PS |
  578. MXL86110_EXT_RGMII_CFG1_TX_10MB_100MB_DELAY_1950PS |
  579. MXL86110_EXT_RGMII_CFG1_RX_DELAY_1950PS;
  580. break;
  581. default:
  582. ret = -EINVAL;
  583. goto out;
  584. }
  585. ret = __mxl86110_modify_extended_reg(phydev,
  586. MXL86110_EXT_RGMII_CFG1_REG,
  587. MXL86110_EXT_RGMII_CFG1_FULL_MASK,
  588. val);
  589. if (ret < 0)
  590. goto out;
  591. /* Configure RXDLY (RGMII Rx Clock Delay) to disable
  592. * the default additional delay value on RX_CLK
  593. * (2 ns for 125 MHz, 8 ns for 25 MHz/2.5 MHz)
  594. * and use just the digital one selected before
  595. */
  596. ret = __mxl86110_modify_extended_reg(phydev,
  597. MXL86110_EXT_CHIP_CFG_REG,
  598. MXL86110_EXT_CHIP_CFG_RXDLY_ENABLE,
  599. 0);
  600. if (ret < 0)
  601. goto out;
  602. out:
  603. return ret;
  604. }
  605. /**
  606. * mxl86110_config_init() - initialize the MXL86110 PHY
  607. * @phydev: pointer to the phy_device
  608. *
  609. * Return: 0 or negative errno code
  610. */
  611. static int mxl86110_config_init(struct phy_device *phydev)
  612. {
  613. int ret;
  614. phy_lock_mdio_bus(phydev);
  615. /* configure syncE / clk output */
  616. ret = mxl86110_synce_clk_cfg(phydev);
  617. if (ret < 0)
  618. goto out;
  619. ret = mxl86110_config_rgmii_delay(phydev);
  620. if (ret < 0)
  621. goto out;
  622. ret = mxl86110_enable_led_activity_blink(phydev);
  623. if (ret < 0)
  624. goto out;
  625. ret = mxl86110_broadcast_cfg(phydev);
  626. out:
  627. phy_unlock_mdio_bus(phydev);
  628. return ret;
  629. }
  630. /**
  631. * mxl86111_probe() - validate bootstrap chip config and set UTP page
  632. * @phydev: pointer to the phy_device
  633. *
  634. * Return: 0 or negative errno code
  635. */
  636. static int mxl86111_probe(struct phy_device *phydev)
  637. {
  638. int chip_config;
  639. u16 reg_page;
  640. int ret;
  641. chip_config = mxl86110_read_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG);
  642. if (chip_config < 0)
  643. return chip_config;
  644. switch (chip_config & MXL86111_EXT_CHIP_CFG_MODE_SEL_MASK) {
  645. case MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_SGMII:
  646. case MXL86111_EXT_CHIP_CFG_MODE_UTP_TO_RGMII:
  647. phydev->port = PORT_TP;
  648. reg_page = MXL86111_EXT_SMI_SDS_PHYUTP_SPACE;
  649. break;
  650. default:
  651. return -EOPNOTSUPP;
  652. }
  653. ret = mxl86110_write_extended_reg(phydev,
  654. MXL86111_EXT_SMI_SDS_PHY_REG,
  655. reg_page);
  656. if (ret < 0)
  657. return ret;
  658. return 0;
  659. }
  660. /**
  661. * mxl86111_config_init() - initialize the MXL86111 PHY
  662. * @phydev: pointer to the phy_device
  663. *
  664. * Return: 0 or negative errno code
  665. */
  666. static int mxl86111_config_init(struct phy_device *phydev)
  667. {
  668. int ret;
  669. phy_lock_mdio_bus(phydev);
  670. /* configure syncE / clk output */
  671. ret = mxl86110_synce_clk_cfg(phydev);
  672. if (ret < 0)
  673. goto out;
  674. switch (phydev->interface) {
  675. case PHY_INTERFACE_MODE_100BASEX:
  676. ret = __mxl86110_modify_extended_reg(phydev,
  677. MXL86111_EXT_MISC_CONFIG_REG,
  678. MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL,
  679. MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_100BX);
  680. if (ret < 0)
  681. goto out;
  682. break;
  683. case PHY_INTERFACE_MODE_1000BASEX:
  684. case PHY_INTERFACE_MODE_SGMII:
  685. ret = __mxl86110_modify_extended_reg(phydev,
  686. MXL86111_EXT_MISC_CONFIG_REG,
  687. MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL,
  688. MXL86111_EXT_MISC_CONFIG_FIB_SPEED_SEL_1000BX);
  689. if (ret < 0)
  690. goto out;
  691. break;
  692. default:
  693. /* RGMII modes */
  694. ret = mxl86110_config_rgmii_delay(phydev);
  695. if (ret < 0)
  696. goto out;
  697. ret = __mxl86110_modify_extended_reg(phydev, MXL86110_EXT_RGMII_CFG1_REG,
  698. MXL86110_EXT_RGMII_CFG1_FULL_MASK, ret);
  699. /* PL P1 requires optimized RGMII timing for 1.8V RGMII voltage
  700. */
  701. ret = __mxl86110_read_extended_reg(phydev, 0xf);
  702. if (ret < 0)
  703. goto out;
  704. if (ret == MXL86111_PL_P1) {
  705. ret = __mxl86110_read_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG);
  706. if (ret < 0)
  707. goto out;
  708. /* check if LDO is in 1.8V mode */
  709. switch (FIELD_GET(MXL86111_EXT_CHIP_CFG_CLDO_MASK, ret)) {
  710. case MXL86111_EXT_CHIP_CFG_CLDO_1V8_3:
  711. case MXL86111_EXT_CHIP_CFG_CLDO_1V8_2:
  712. ret = __mxl86110_write_extended_reg(phydev, 0xa010, 0xabff);
  713. if (ret < 0)
  714. goto out;
  715. break;
  716. default:
  717. break;
  718. }
  719. }
  720. break;
  721. }
  722. ret = mxl86110_enable_led_activity_blink(phydev);
  723. if (ret < 0)
  724. goto out;
  725. ret = mxl86110_broadcast_cfg(phydev);
  726. out:
  727. phy_unlock_mdio_bus(phydev);
  728. return ret;
  729. }
  730. /**
  731. * mxl86111_read_page() - read reg page
  732. * @phydev: pointer to the phy_device
  733. *
  734. * Return: current reg space of mxl86111 or negative errno code
  735. */
  736. static int mxl86111_read_page(struct phy_device *phydev)
  737. {
  738. int page;
  739. page = __mxl86110_read_extended_reg(phydev, MXL86111_EXT_SMI_SDS_PHY_REG);
  740. if (page < 0)
  741. return page;
  742. return page & MXL86111_EXT_SMI_SDS_PHYSPACE_MASK;
  743. };
  744. /**
  745. * mxl86111_write_page() - Set reg page
  746. * @phydev: pointer to the phy_device
  747. * @page: The reg page to set
  748. *
  749. * Return: 0 or negative errno code
  750. */
  751. static int mxl86111_write_page(struct phy_device *phydev, int page)
  752. {
  753. return __mxl86110_modify_extended_reg(phydev, MXL86111_EXT_SMI_SDS_PHY_REG,
  754. MXL86111_EXT_SMI_SDS_PHYSPACE_MASK, page);
  755. };
  756. static int mxl86111_config_inband(struct phy_device *phydev, unsigned int modes)
  757. {
  758. int ret;
  759. ret = phy_modify_paged(phydev, MXL86111_EXT_SMI_SDS_PHYFIBER_SPACE,
  760. MII_BMCR, BMCR_ANENABLE,
  761. (modes == LINK_INBAND_DISABLE) ? 0 : BMCR_ANENABLE);
  762. if (ret < 0)
  763. goto out;
  764. phy_lock_mdio_bus(phydev);
  765. ret = __mxl86110_modify_extended_reg(phydev, MXL86111_EXT_SDS_LINK_TIMER_CFG2_REG,
  766. MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN,
  767. (modes == LINK_INBAND_DISABLE) ? 0 :
  768. MXL86111_EXT_SDS_LINK_TIMER_CFG2_EN_AUTOSEN);
  769. if (ret < 0)
  770. goto out;
  771. ret = __mxl86110_modify_extended_reg(phydev, MXL86110_EXT_CHIP_CFG_REG,
  772. MXL86110_EXT_CHIP_CFG_SW_RST_N_MODE, 0);
  773. if (ret < 0)
  774. goto out;
  775. /* For fiber forced mode, power down/up to re-aneg */
  776. if (modes != LINK_INBAND_DISABLE) {
  777. __phy_modify(phydev, MII_BMCR, 0, BMCR_PDOWN);
  778. usleep_range(1000, 1050);
  779. __phy_modify(phydev, MII_BMCR, BMCR_PDOWN, 0);
  780. }
  781. out:
  782. phy_unlock_mdio_bus(phydev);
  783. return ret;
  784. }
  785. static unsigned int mxl86111_inband_caps(struct phy_device *phydev,
  786. phy_interface_t interface)
  787. {
  788. switch (interface) {
  789. case PHY_INTERFACE_MODE_100BASEX:
  790. case PHY_INTERFACE_MODE_1000BASEX:
  791. case PHY_INTERFACE_MODE_SGMII:
  792. return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
  793. default:
  794. return 0;
  795. }
  796. }
  797. static struct phy_driver mxl_phy_drvs[] = {
  798. {
  799. PHY_ID_MATCH_EXACT(PHY_ID_MXL86110),
  800. .name = "MXL86110 Gigabit Ethernet",
  801. .config_init = mxl86110_config_init,
  802. .suspend = genphy_suspend,
  803. .resume = genphy_resume,
  804. .soft_reset = genphy_soft_reset,
  805. .get_wol = mxl86110_get_wol,
  806. .set_wol = mxl86110_set_wol,
  807. .led_brightness_set = mxl86110_led_brightness_set,
  808. .led_hw_is_supported = mxl86110_led_hw_is_supported,
  809. .led_hw_control_get = mxl86110_led_hw_control_get,
  810. .led_hw_control_set = mxl86110_led_hw_control_set,
  811. },
  812. {
  813. PHY_ID_MATCH_EXACT(PHY_ID_MXL86111),
  814. .name = "MXL86111 Gigabit Ethernet",
  815. .probe = mxl86111_probe,
  816. .config_init = mxl86111_config_init,
  817. .get_wol = mxl86110_get_wol,
  818. .set_wol = mxl86110_set_wol,
  819. .inband_caps = mxl86111_inband_caps,
  820. .config_inband = mxl86111_config_inband,
  821. .read_page = mxl86111_read_page,
  822. .write_page = mxl86111_write_page,
  823. .led_brightness_set = mxl86110_led_brightness_set,
  824. .led_hw_is_supported = mxl86110_led_hw_is_supported,
  825. .led_hw_control_get = mxl86110_led_hw_control_get,
  826. .led_hw_control_set = mxl86110_led_hw_control_set,
  827. },
  828. };
  829. module_phy_driver(mxl_phy_drvs);
  830. static const struct mdio_device_id __maybe_unused mxl_tbl[] = {
  831. { PHY_ID_MATCH_EXACT(PHY_ID_MXL86110) },
  832. { PHY_ID_MATCH_EXACT(PHY_ID_MXL86111) },
  833. { }
  834. };
  835. MODULE_DEVICE_TABLE(mdio, mxl_tbl);
  836. MODULE_DESCRIPTION("MaxLinear MXL86110/MXL86111 PHY driver");
  837. MODULE_AUTHOR("Stefano Radaelli");
  838. MODULE_LICENSE("GPL");