ethtool.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * Copyright (c) 2013 Johannes Berg <johannes@sipsolutions.net>
  3. *
  4. * This file is free software: you may copy, redistribute and/or modify it
  5. * under the terms of the GNU General Public License as published by the
  6. * Free Software Foundation, either version 2 of the License, or (at your
  7. * option) any later version.
  8. *
  9. * This file is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. * This file incorporates work covered by the following copyright and
  18. * permission notice:
  19. *
  20. * Copyright (c) 2012 Qualcomm Atheros, Inc.
  21. *
  22. * Permission to use, copy, modify, and/or distribute this software for any
  23. * purpose with or without fee is hereby granted, provided that the above
  24. * copyright notice and this permission notice appear in all copies.
  25. *
  26. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  27. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  28. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  29. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  30. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  31. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  32. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33. */
  34. #include <linux/pci.h>
  35. #include <linux/ip.h>
  36. #include <linux/tcp.h>
  37. #include <linux/netdevice.h>
  38. #include <linux/etherdevice.h>
  39. #include <linux/ethtool.h>
  40. #include <linux/mdio.h>
  41. #include <linux/interrupt.h>
  42. #include <asm/byteorder.h>
  43. #include "alx.h"
  44. #include "reg.h"
  45. #include "hw.h"
  46. static u32 alx_get_supported_speeds(struct alx_hw *hw)
  47. {
  48. u32 supported = SUPPORTED_10baseT_Half |
  49. SUPPORTED_10baseT_Full |
  50. SUPPORTED_100baseT_Half |
  51. SUPPORTED_100baseT_Full;
  52. if (alx_hw_giga(hw))
  53. supported |= SUPPORTED_1000baseT_Full;
  54. BUILD_BUG_ON(SUPPORTED_10baseT_Half != ADVERTISED_10baseT_Half);
  55. BUILD_BUG_ON(SUPPORTED_10baseT_Full != ADVERTISED_10baseT_Full);
  56. BUILD_BUG_ON(SUPPORTED_100baseT_Half != ADVERTISED_100baseT_Half);
  57. BUILD_BUG_ON(SUPPORTED_100baseT_Full != ADVERTISED_100baseT_Full);
  58. BUILD_BUG_ON(SUPPORTED_1000baseT_Full != ADVERTISED_1000baseT_Full);
  59. return supported;
  60. }
  61. static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
  62. {
  63. struct alx_priv *alx = netdev_priv(netdev);
  64. struct alx_hw *hw = &alx->hw;
  65. ecmd->supported = SUPPORTED_Autoneg |
  66. SUPPORTED_TP |
  67. SUPPORTED_Pause |
  68. SUPPORTED_Asym_Pause;
  69. if (alx_hw_giga(hw))
  70. ecmd->supported |= SUPPORTED_1000baseT_Full;
  71. ecmd->supported |= alx_get_supported_speeds(hw);
  72. ecmd->advertising = ADVERTISED_TP;
  73. if (hw->adv_cfg & ADVERTISED_Autoneg)
  74. ecmd->advertising |= hw->adv_cfg;
  75. ecmd->port = PORT_TP;
  76. ecmd->phy_address = 0;
  77. if (hw->adv_cfg & ADVERTISED_Autoneg)
  78. ecmd->autoneg = AUTONEG_ENABLE;
  79. else
  80. ecmd->autoneg = AUTONEG_DISABLE;
  81. ecmd->transceiver = XCVR_INTERNAL;
  82. if (hw->flowctrl & ALX_FC_ANEG && hw->adv_cfg & ADVERTISED_Autoneg) {
  83. if (hw->flowctrl & ALX_FC_RX) {
  84. ecmd->advertising |= ADVERTISED_Pause;
  85. if (!(hw->flowctrl & ALX_FC_TX))
  86. ecmd->advertising |= ADVERTISED_Asym_Pause;
  87. } else if (hw->flowctrl & ALX_FC_TX) {
  88. ecmd->advertising |= ADVERTISED_Asym_Pause;
  89. }
  90. }
  91. ethtool_cmd_speed_set(ecmd, hw->link_speed);
  92. ecmd->duplex = hw->duplex;
  93. return 0;
  94. }
  95. static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
  96. {
  97. struct alx_priv *alx = netdev_priv(netdev);
  98. struct alx_hw *hw = &alx->hw;
  99. u32 adv_cfg;
  100. ASSERT_RTNL();
  101. if (ecmd->autoneg == AUTONEG_ENABLE) {
  102. if (ecmd->advertising & ~alx_get_supported_speeds(hw))
  103. return -EINVAL;
  104. adv_cfg = ecmd->advertising | ADVERTISED_Autoneg;
  105. } else {
  106. adv_cfg = alx_speed_to_ethadv(ethtool_cmd_speed(ecmd),
  107. ecmd->duplex);
  108. if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full)
  109. return -EINVAL;
  110. }
  111. hw->adv_cfg = adv_cfg;
  112. return alx_setup_speed_duplex(hw, adv_cfg, hw->flowctrl);
  113. }
  114. static void alx_get_pauseparam(struct net_device *netdev,
  115. struct ethtool_pauseparam *pause)
  116. {
  117. struct alx_priv *alx = netdev_priv(netdev);
  118. struct alx_hw *hw = &alx->hw;
  119. pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG &&
  120. hw->adv_cfg & ADVERTISED_Autoneg);
  121. pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX);
  122. pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX);
  123. }
  124. static int alx_set_pauseparam(struct net_device *netdev,
  125. struct ethtool_pauseparam *pause)
  126. {
  127. struct alx_priv *alx = netdev_priv(netdev);
  128. struct alx_hw *hw = &alx->hw;
  129. int err = 0;
  130. bool reconfig_phy = false;
  131. u8 fc = 0;
  132. if (pause->tx_pause)
  133. fc |= ALX_FC_TX;
  134. if (pause->rx_pause)
  135. fc |= ALX_FC_RX;
  136. if (pause->autoneg)
  137. fc |= ALX_FC_ANEG;
  138. ASSERT_RTNL();
  139. /* restart auto-neg for auto-mode */
  140. if (hw->adv_cfg & ADVERTISED_Autoneg) {
  141. if (!((fc ^ hw->flowctrl) & ALX_FC_ANEG))
  142. reconfig_phy = true;
  143. if (fc & hw->flowctrl & ALX_FC_ANEG &&
  144. (fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX))
  145. reconfig_phy = true;
  146. }
  147. if (reconfig_phy) {
  148. err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc);
  149. if (err)
  150. return err;
  151. }
  152. /* flow control on mac */
  153. if ((fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX))
  154. alx_cfg_mac_flowcontrol(hw, fc);
  155. hw->flowctrl = fc;
  156. return 0;
  157. }
  158. static u32 alx_get_msglevel(struct net_device *netdev)
  159. {
  160. struct alx_priv *alx = netdev_priv(netdev);
  161. return alx->msg_enable;
  162. }
  163. static void alx_set_msglevel(struct net_device *netdev, u32 data)
  164. {
  165. struct alx_priv *alx = netdev_priv(netdev);
  166. alx->msg_enable = data;
  167. }
  168. const struct ethtool_ops alx_ethtool_ops = {
  169. .get_settings = alx_get_settings,
  170. .set_settings = alx_set_settings,
  171. .get_pauseparam = alx_get_pauseparam,
  172. .set_pauseparam = alx_set_pauseparam,
  173. .get_msglevel = alx_get_msglevel,
  174. .set_msglevel = alx_set_msglevel,
  175. .get_link = ethtool_op_get_link,
  176. };