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 
35 #include <linux/pci.h>
36 #include <linux/ip.h>
37 #include <linux/tcp.h>
38 #include <linux/netdevice.h>
39 #include <linux/etherdevice.h>
40 #include <linux/ethtool.h>
41 #include <linux/mdio.h>
42 #include <linux/interrupt.h>
43 #include <asm/byteorder.h>
44 
45 #include "alx.h"
46 #include "reg.h"
47 #include "hw.h"
48 
49 static u32 alx_get_supported_speeds(struct alx_hw *hw)
50 {
51 	u32 supported = SUPPORTED_10baseT_Half |
52 			SUPPORTED_10baseT_Full |
53 			SUPPORTED_100baseT_Half |
54 			SUPPORTED_100baseT_Full;
55 
56 	if (alx_hw_giga(hw))
57 		supported |= SUPPORTED_1000baseT_Full;
58 
59 	BUILD_BUG_ON(SUPPORTED_10baseT_Half != ADVERTISED_10baseT_Half);
60 	BUILD_BUG_ON(SUPPORTED_10baseT_Full != ADVERTISED_10baseT_Full);
61 	BUILD_BUG_ON(SUPPORTED_100baseT_Half != ADVERTISED_100baseT_Half);
62 	BUILD_BUG_ON(SUPPORTED_100baseT_Full != ADVERTISED_100baseT_Full);
63 	BUILD_BUG_ON(SUPPORTED_1000baseT_Full != ADVERTISED_1000baseT_Full);
64 
65 	return supported;
66 }
67 
68 static int alx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
69 {
70 	struct alx_priv *alx = netdev_priv(netdev);
71 	struct alx_hw *hw = &alx->hw;
72 
73 	ecmd->supported = SUPPORTED_Autoneg |
74 			  SUPPORTED_TP |
75 			  SUPPORTED_Pause |
76 			  SUPPORTED_Asym_Pause;
77 	if (alx_hw_giga(hw))
78 		ecmd->supported |= SUPPORTED_1000baseT_Full;
79 	ecmd->supported |= alx_get_supported_speeds(hw);
80 
81 	ecmd->advertising = ADVERTISED_TP;
82 	if (hw->adv_cfg & ADVERTISED_Autoneg)
83 		ecmd->advertising |= hw->adv_cfg;
84 
85 	ecmd->port = PORT_TP;
86 	ecmd->phy_address = 0;
87 
88 	if (hw->adv_cfg & ADVERTISED_Autoneg)
89 		ecmd->autoneg = AUTONEG_ENABLE;
90 	else
91 		ecmd->autoneg = AUTONEG_DISABLE;
92 	ecmd->transceiver = XCVR_INTERNAL;
93 
94 	if (hw->flowctrl & ALX_FC_ANEG && hw->adv_cfg & ADVERTISED_Autoneg) {
95 		if (hw->flowctrl & ALX_FC_RX) {
96 			ecmd->advertising |= ADVERTISED_Pause;
97 
98 			if (!(hw->flowctrl & ALX_FC_TX))
99 				ecmd->advertising |= ADVERTISED_Asym_Pause;
100 		} else if (hw->flowctrl & ALX_FC_TX) {
101 			ecmd->advertising |= ADVERTISED_Asym_Pause;
102 		}
103 	}
104 
105 	ethtool_cmd_speed_set(ecmd, hw->link_speed);
106 	ecmd->duplex = hw->duplex;
107 
108 	return 0;
109 }
110 
111 static int alx_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
112 {
113 	struct alx_priv *alx = netdev_priv(netdev);
114 	struct alx_hw *hw = &alx->hw;
115 	u32 adv_cfg;
116 
117 	ASSERT_RTNL();
118 
119 	if (ecmd->autoneg == AUTONEG_ENABLE) {
120 		if (ecmd->advertising & ~alx_get_supported_speeds(hw))
121 			return -EINVAL;
122 		adv_cfg = ecmd->advertising | ADVERTISED_Autoneg;
123 	} else {
124 		adv_cfg = alx_speed_to_ethadv(ethtool_cmd_speed(ecmd),
125 					      ecmd->duplex);
126 
127 		if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full)
128 			return -EINVAL;
129 	}
130 
131 	hw->adv_cfg = adv_cfg;
132 	return alx_setup_speed_duplex(hw, adv_cfg, hw->flowctrl);
133 }
134 
135 static void alx_get_pauseparam(struct net_device *netdev,
136 			       struct ethtool_pauseparam *pause)
137 {
138 	struct alx_priv *alx = netdev_priv(netdev);
139 	struct alx_hw *hw = &alx->hw;
140 
141 	pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG &&
142 			    hw->adv_cfg & ADVERTISED_Autoneg);
143 	pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX);
144 	pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX);
145 }
146 
147 
148 static int alx_set_pauseparam(struct net_device *netdev,
149 			      struct ethtool_pauseparam *pause)
150 {
151 	struct alx_priv *alx = netdev_priv(netdev);
152 	struct alx_hw *hw = &alx->hw;
153 	int err = 0;
154 	bool reconfig_phy = false;
155 	u8 fc = 0;
156 
157 	if (pause->tx_pause)
158 		fc |= ALX_FC_TX;
159 	if (pause->rx_pause)
160 		fc |= ALX_FC_RX;
161 	if (pause->autoneg)
162 		fc |= ALX_FC_ANEG;
163 
164 	ASSERT_RTNL();
165 
166 	/* restart auto-neg for auto-mode */
167 	if (hw->adv_cfg & ADVERTISED_Autoneg) {
168 		if (!((fc ^ hw->flowctrl) & ALX_FC_ANEG))
169 			reconfig_phy = true;
170 		if (fc & hw->flowctrl & ALX_FC_ANEG &&
171 		    (fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX))
172 			reconfig_phy = true;
173 	}
174 
175 	if (reconfig_phy) {
176 		err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc);
177 		if (err)
178 			return err;
179 	}
180 
181 	/* flow control on mac */
182 	if ((fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX))
183 		alx_cfg_mac_flowcontrol(hw, fc);
184 
185 	hw->flowctrl = fc;
186 
187 	return 0;
188 }
189 
190 static u32 alx_get_msglevel(struct net_device *netdev)
191 {
192 	struct alx_priv *alx = netdev_priv(netdev);
193 
194 	return alx->msg_enable;
195 }
196 
197 static void alx_set_msglevel(struct net_device *netdev, u32 data)
198 {
199 	struct alx_priv *alx = netdev_priv(netdev);
200 
201 	alx->msg_enable = data;
202 }
203 
204 const struct ethtool_ops alx_ethtool_ops = {
205 	.get_settings	= alx_get_settings,
206 	.set_settings	= alx_set_settings,
207 	.get_pauseparam	= alx_get_pauseparam,
208 	.set_pauseparam	= alx_set_pauseparam,
209 	.get_msglevel	= alx_get_msglevel,
210 	.set_msglevel	= alx_set_msglevel,
211 	.get_link	= ethtool_op_get_link,
212 };
213