1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright (c) 2013 Johannes Berg <johannes@sipsolutions.net>
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * This file is free software: you may copy, redistribute and/or modify it
5*4882a593Smuzhiyun * under the terms of the GNU General Public License as published by the
6*4882a593Smuzhiyun * Free Software Foundation, either version 2 of the License, or (at your
7*4882a593Smuzhiyun * option) any later version.
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * This file is distributed in the hope that it will be useful, but
10*4882a593Smuzhiyun * WITHOUT ANY WARRANTY; without even the implied warranty of
11*4882a593Smuzhiyun * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12*4882a593Smuzhiyun * General Public License for more details.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * You should have received a copy of the GNU General Public License
15*4882a593Smuzhiyun * along with this program. If not, see <http://www.gnu.org/licenses/>.
16*4882a593Smuzhiyun *
17*4882a593Smuzhiyun * This file incorporates work covered by the following copyright and
18*4882a593Smuzhiyun * permission notice:
19*4882a593Smuzhiyun *
20*4882a593Smuzhiyun * Copyright (c) 2012 Qualcomm Atheros, Inc.
21*4882a593Smuzhiyun *
22*4882a593Smuzhiyun * Permission to use, copy, modify, and/or distribute this software for any
23*4882a593Smuzhiyun * purpose with or without fee is hereby granted, provided that the above
24*4882a593Smuzhiyun * copyright notice and this permission notice appear in all copies.
25*4882a593Smuzhiyun *
26*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
27*4882a593Smuzhiyun * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
28*4882a593Smuzhiyun * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
29*4882a593Smuzhiyun * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
30*4882a593Smuzhiyun * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
31*4882a593Smuzhiyun * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
32*4882a593Smuzhiyun * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
33*4882a593Smuzhiyun */
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #include <linux/pci.h>
36*4882a593Smuzhiyun #include <linux/ip.h>
37*4882a593Smuzhiyun #include <linux/tcp.h>
38*4882a593Smuzhiyun #include <linux/netdevice.h>
39*4882a593Smuzhiyun #include <linux/etherdevice.h>
40*4882a593Smuzhiyun #include <linux/ethtool.h>
41*4882a593Smuzhiyun #include <linux/mdio.h>
42*4882a593Smuzhiyun #include <linux/interrupt.h>
43*4882a593Smuzhiyun #include <asm/byteorder.h>
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun #include "alx.h"
46*4882a593Smuzhiyun #include "reg.h"
47*4882a593Smuzhiyun #include "hw.h"
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /* The order of these strings must match the order of the fields in
50*4882a593Smuzhiyun * struct alx_hw_stats
51*4882a593Smuzhiyun * See hw.h
52*4882a593Smuzhiyun */
53*4882a593Smuzhiyun static const char alx_gstrings_stats[][ETH_GSTRING_LEN] = {
54*4882a593Smuzhiyun "rx_packets",
55*4882a593Smuzhiyun "rx_bcast_packets",
56*4882a593Smuzhiyun "rx_mcast_packets",
57*4882a593Smuzhiyun "rx_pause_packets",
58*4882a593Smuzhiyun "rx_ctrl_packets",
59*4882a593Smuzhiyun "rx_fcs_errors",
60*4882a593Smuzhiyun "rx_length_errors",
61*4882a593Smuzhiyun "rx_bytes",
62*4882a593Smuzhiyun "rx_runt_packets",
63*4882a593Smuzhiyun "rx_fragments",
64*4882a593Smuzhiyun "rx_64B_or_less_packets",
65*4882a593Smuzhiyun "rx_65B_to_127B_packets",
66*4882a593Smuzhiyun "rx_128B_to_255B_packets",
67*4882a593Smuzhiyun "rx_256B_to_511B_packets",
68*4882a593Smuzhiyun "rx_512B_to_1023B_packets",
69*4882a593Smuzhiyun "rx_1024B_to_1518B_packets",
70*4882a593Smuzhiyun "rx_1519B_to_mtu_packets",
71*4882a593Smuzhiyun "rx_oversize_packets",
72*4882a593Smuzhiyun "rx_rxf_ov_drop_packets",
73*4882a593Smuzhiyun "rx_rrd_ov_drop_packets",
74*4882a593Smuzhiyun "rx_align_errors",
75*4882a593Smuzhiyun "rx_bcast_bytes",
76*4882a593Smuzhiyun "rx_mcast_bytes",
77*4882a593Smuzhiyun "rx_address_errors",
78*4882a593Smuzhiyun "tx_packets",
79*4882a593Smuzhiyun "tx_bcast_packets",
80*4882a593Smuzhiyun "tx_mcast_packets",
81*4882a593Smuzhiyun "tx_pause_packets",
82*4882a593Smuzhiyun "tx_exc_defer_packets",
83*4882a593Smuzhiyun "tx_ctrl_packets",
84*4882a593Smuzhiyun "tx_defer_packets",
85*4882a593Smuzhiyun "tx_bytes",
86*4882a593Smuzhiyun "tx_64B_or_less_packets",
87*4882a593Smuzhiyun "tx_65B_to_127B_packets",
88*4882a593Smuzhiyun "tx_128B_to_255B_packets",
89*4882a593Smuzhiyun "tx_256B_to_511B_packets",
90*4882a593Smuzhiyun "tx_512B_to_1023B_packets",
91*4882a593Smuzhiyun "tx_1024B_to_1518B_packets",
92*4882a593Smuzhiyun "tx_1519B_to_mtu_packets",
93*4882a593Smuzhiyun "tx_single_collision",
94*4882a593Smuzhiyun "tx_multiple_collisions",
95*4882a593Smuzhiyun "tx_late_collision",
96*4882a593Smuzhiyun "tx_abort_collision",
97*4882a593Smuzhiyun "tx_underrun",
98*4882a593Smuzhiyun "tx_trd_eop",
99*4882a593Smuzhiyun "tx_length_errors",
100*4882a593Smuzhiyun "tx_trunc_packets",
101*4882a593Smuzhiyun "tx_bcast_bytes",
102*4882a593Smuzhiyun "tx_mcast_bytes",
103*4882a593Smuzhiyun "tx_update",
104*4882a593Smuzhiyun };
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun #define ALX_NUM_STATS ARRAY_SIZE(alx_gstrings_stats)
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun
alx_get_supported_speeds(struct alx_hw * hw)109*4882a593Smuzhiyun static u32 alx_get_supported_speeds(struct alx_hw *hw)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun u32 supported = SUPPORTED_10baseT_Half |
112*4882a593Smuzhiyun SUPPORTED_10baseT_Full |
113*4882a593Smuzhiyun SUPPORTED_100baseT_Half |
114*4882a593Smuzhiyun SUPPORTED_100baseT_Full;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (alx_hw_giga(hw))
117*4882a593Smuzhiyun supported |= SUPPORTED_1000baseT_Full;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun BUILD_BUG_ON(SUPPORTED_10baseT_Half != ADVERTISED_10baseT_Half);
120*4882a593Smuzhiyun BUILD_BUG_ON(SUPPORTED_10baseT_Full != ADVERTISED_10baseT_Full);
121*4882a593Smuzhiyun BUILD_BUG_ON(SUPPORTED_100baseT_Half != ADVERTISED_100baseT_Half);
122*4882a593Smuzhiyun BUILD_BUG_ON(SUPPORTED_100baseT_Full != ADVERTISED_100baseT_Full);
123*4882a593Smuzhiyun BUILD_BUG_ON(SUPPORTED_1000baseT_Full != ADVERTISED_1000baseT_Full);
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun return supported;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun
alx_get_link_ksettings(struct net_device * netdev,struct ethtool_link_ksettings * cmd)128*4882a593Smuzhiyun static int alx_get_link_ksettings(struct net_device *netdev,
129*4882a593Smuzhiyun struct ethtool_link_ksettings *cmd)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun struct alx_priv *alx = netdev_priv(netdev);
132*4882a593Smuzhiyun struct alx_hw *hw = &alx->hw;
133*4882a593Smuzhiyun u32 supported, advertising;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun supported = SUPPORTED_Autoneg |
136*4882a593Smuzhiyun SUPPORTED_TP |
137*4882a593Smuzhiyun SUPPORTED_Pause |
138*4882a593Smuzhiyun SUPPORTED_Asym_Pause;
139*4882a593Smuzhiyun if (alx_hw_giga(hw))
140*4882a593Smuzhiyun supported |= SUPPORTED_1000baseT_Full;
141*4882a593Smuzhiyun supported |= alx_get_supported_speeds(hw);
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun advertising = ADVERTISED_TP;
144*4882a593Smuzhiyun if (hw->adv_cfg & ADVERTISED_Autoneg)
145*4882a593Smuzhiyun advertising |= hw->adv_cfg;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun cmd->base.port = PORT_TP;
148*4882a593Smuzhiyun cmd->base.phy_address = 0;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun if (hw->adv_cfg & ADVERTISED_Autoneg)
151*4882a593Smuzhiyun cmd->base.autoneg = AUTONEG_ENABLE;
152*4882a593Smuzhiyun else
153*4882a593Smuzhiyun cmd->base.autoneg = AUTONEG_DISABLE;
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun if (hw->flowctrl & ALX_FC_ANEG && hw->adv_cfg & ADVERTISED_Autoneg) {
156*4882a593Smuzhiyun if (hw->flowctrl & ALX_FC_RX) {
157*4882a593Smuzhiyun advertising |= ADVERTISED_Pause;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun if (!(hw->flowctrl & ALX_FC_TX))
160*4882a593Smuzhiyun advertising |= ADVERTISED_Asym_Pause;
161*4882a593Smuzhiyun } else if (hw->flowctrl & ALX_FC_TX) {
162*4882a593Smuzhiyun advertising |= ADVERTISED_Asym_Pause;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun cmd->base.speed = hw->link_speed;
167*4882a593Smuzhiyun cmd->base.duplex = hw->duplex;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
170*4882a593Smuzhiyun supported);
171*4882a593Smuzhiyun ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
172*4882a593Smuzhiyun advertising);
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun return 0;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
alx_set_link_ksettings(struct net_device * netdev,const struct ethtool_link_ksettings * cmd)177*4882a593Smuzhiyun static int alx_set_link_ksettings(struct net_device *netdev,
178*4882a593Smuzhiyun const struct ethtool_link_ksettings *cmd)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun struct alx_priv *alx = netdev_priv(netdev);
181*4882a593Smuzhiyun struct alx_hw *hw = &alx->hw;
182*4882a593Smuzhiyun u32 adv_cfg;
183*4882a593Smuzhiyun u32 advertising;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun ASSERT_RTNL();
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun ethtool_convert_link_mode_to_legacy_u32(&advertising,
188*4882a593Smuzhiyun cmd->link_modes.advertising);
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun if (cmd->base.autoneg == AUTONEG_ENABLE) {
191*4882a593Smuzhiyun if (advertising & ~alx_get_supported_speeds(hw))
192*4882a593Smuzhiyun return -EINVAL;
193*4882a593Smuzhiyun adv_cfg = advertising | ADVERTISED_Autoneg;
194*4882a593Smuzhiyun } else {
195*4882a593Smuzhiyun adv_cfg = alx_speed_to_ethadv(cmd->base.speed,
196*4882a593Smuzhiyun cmd->base.duplex);
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun if (!adv_cfg || adv_cfg == ADVERTISED_1000baseT_Full)
199*4882a593Smuzhiyun return -EINVAL;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun hw->adv_cfg = adv_cfg;
203*4882a593Smuzhiyun return alx_setup_speed_duplex(hw, adv_cfg, hw->flowctrl);
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
alx_get_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)206*4882a593Smuzhiyun static void alx_get_pauseparam(struct net_device *netdev,
207*4882a593Smuzhiyun struct ethtool_pauseparam *pause)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun struct alx_priv *alx = netdev_priv(netdev);
210*4882a593Smuzhiyun struct alx_hw *hw = &alx->hw;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun pause->autoneg = !!(hw->flowctrl & ALX_FC_ANEG &&
213*4882a593Smuzhiyun hw->adv_cfg & ADVERTISED_Autoneg);
214*4882a593Smuzhiyun pause->tx_pause = !!(hw->flowctrl & ALX_FC_TX);
215*4882a593Smuzhiyun pause->rx_pause = !!(hw->flowctrl & ALX_FC_RX);
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun
alx_set_pauseparam(struct net_device * netdev,struct ethtool_pauseparam * pause)219*4882a593Smuzhiyun static int alx_set_pauseparam(struct net_device *netdev,
220*4882a593Smuzhiyun struct ethtool_pauseparam *pause)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun struct alx_priv *alx = netdev_priv(netdev);
223*4882a593Smuzhiyun struct alx_hw *hw = &alx->hw;
224*4882a593Smuzhiyun int err = 0;
225*4882a593Smuzhiyun bool reconfig_phy = false;
226*4882a593Smuzhiyun u8 fc = 0;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun if (pause->tx_pause)
229*4882a593Smuzhiyun fc |= ALX_FC_TX;
230*4882a593Smuzhiyun if (pause->rx_pause)
231*4882a593Smuzhiyun fc |= ALX_FC_RX;
232*4882a593Smuzhiyun if (pause->autoneg)
233*4882a593Smuzhiyun fc |= ALX_FC_ANEG;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun ASSERT_RTNL();
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun /* restart auto-neg for auto-mode */
238*4882a593Smuzhiyun if (hw->adv_cfg & ADVERTISED_Autoneg) {
239*4882a593Smuzhiyun if (!((fc ^ hw->flowctrl) & ALX_FC_ANEG))
240*4882a593Smuzhiyun reconfig_phy = true;
241*4882a593Smuzhiyun if (fc & hw->flowctrl & ALX_FC_ANEG &&
242*4882a593Smuzhiyun (fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX))
243*4882a593Smuzhiyun reconfig_phy = true;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun if (reconfig_phy) {
247*4882a593Smuzhiyun err = alx_setup_speed_duplex(hw, hw->adv_cfg, fc);
248*4882a593Smuzhiyun if (err)
249*4882a593Smuzhiyun return err;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun /* flow control on mac */
253*4882a593Smuzhiyun if ((fc ^ hw->flowctrl) & (ALX_FC_RX | ALX_FC_TX))
254*4882a593Smuzhiyun alx_cfg_mac_flowcontrol(hw, fc);
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun hw->flowctrl = fc;
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun return 0;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun
alx_get_msglevel(struct net_device * netdev)261*4882a593Smuzhiyun static u32 alx_get_msglevel(struct net_device *netdev)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun struct alx_priv *alx = netdev_priv(netdev);
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun return alx->msg_enable;
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun
alx_set_msglevel(struct net_device * netdev,u32 data)268*4882a593Smuzhiyun static void alx_set_msglevel(struct net_device *netdev, u32 data)
269*4882a593Smuzhiyun {
270*4882a593Smuzhiyun struct alx_priv *alx = netdev_priv(netdev);
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun alx->msg_enable = data;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun
alx_get_ethtool_stats(struct net_device * netdev,struct ethtool_stats * estats,u64 * data)275*4882a593Smuzhiyun static void alx_get_ethtool_stats(struct net_device *netdev,
276*4882a593Smuzhiyun struct ethtool_stats *estats, u64 *data)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun struct alx_priv *alx = netdev_priv(netdev);
279*4882a593Smuzhiyun struct alx_hw *hw = &alx->hw;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun spin_lock(&alx->stats_lock);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun alx_update_hw_stats(hw);
284*4882a593Smuzhiyun BUILD_BUG_ON(sizeof(hw->stats) - offsetof(struct alx_hw_stats, rx_ok) <
285*4882a593Smuzhiyun ALX_NUM_STATS * sizeof(u64));
286*4882a593Smuzhiyun memcpy(data, &hw->stats.rx_ok, ALX_NUM_STATS * sizeof(u64));
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun spin_unlock(&alx->stats_lock);
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun
alx_get_strings(struct net_device * netdev,u32 stringset,u8 * buf)291*4882a593Smuzhiyun static void alx_get_strings(struct net_device *netdev, u32 stringset, u8 *buf)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun switch (stringset) {
294*4882a593Smuzhiyun case ETH_SS_STATS:
295*4882a593Smuzhiyun memcpy(buf, &alx_gstrings_stats, sizeof(alx_gstrings_stats));
296*4882a593Smuzhiyun break;
297*4882a593Smuzhiyun default:
298*4882a593Smuzhiyun WARN_ON(1);
299*4882a593Smuzhiyun break;
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
alx_get_sset_count(struct net_device * netdev,int sset)303*4882a593Smuzhiyun static int alx_get_sset_count(struct net_device *netdev, int sset)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun switch (sset) {
306*4882a593Smuzhiyun case ETH_SS_STATS:
307*4882a593Smuzhiyun return ALX_NUM_STATS;
308*4882a593Smuzhiyun default:
309*4882a593Smuzhiyun return -EINVAL;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun const struct ethtool_ops alx_ethtool_ops = {
314*4882a593Smuzhiyun .get_pauseparam = alx_get_pauseparam,
315*4882a593Smuzhiyun .set_pauseparam = alx_set_pauseparam,
316*4882a593Smuzhiyun .get_msglevel = alx_get_msglevel,
317*4882a593Smuzhiyun .set_msglevel = alx_set_msglevel,
318*4882a593Smuzhiyun .get_link = ethtool_op_get_link,
319*4882a593Smuzhiyun .get_strings = alx_get_strings,
320*4882a593Smuzhiyun .get_sset_count = alx_get_sset_count,
321*4882a593Smuzhiyun .get_ethtool_stats = alx_get_ethtool_stats,
322*4882a593Smuzhiyun .get_link_ksettings = alx_get_link_ksettings,
323*4882a593Smuzhiyun .set_link_ksettings = alx_set_link_ksettings,
324*4882a593Smuzhiyun };
325