1b2b3212bSRazvan Stefanescu // SPDX-License-Identifier: GPL-2.0
2b2b3212bSRazvan Stefanescu /*
3b2b3212bSRazvan Stefanescu * DPAA2 Ethernet Switch ethtool support
4b2b3212bSRazvan Stefanescu *
5b2b3212bSRazvan Stefanescu * Copyright 2014-2016 Freescale Semiconductor Inc.
6b2b3212bSRazvan Stefanescu * Copyright 2017-2018 NXP
7b2b3212bSRazvan Stefanescu *
8b2b3212bSRazvan Stefanescu */
9b2b3212bSRazvan Stefanescu
10cc69837fSJakub Kicinski #include <linux/ethtool.h>
11cc69837fSJakub Kicinski
12f48298d3SIoana Ciornei #include "dpaa2-switch.h"
13b2b3212bSRazvan Stefanescu
14b2b3212bSRazvan Stefanescu static struct {
15b2b3212bSRazvan Stefanescu enum dpsw_counter id;
16b2b3212bSRazvan Stefanescu char name[ETH_GSTRING_LEN];
175ad71958SIoana Ciornei } dpaa2_switch_ethtool_counters[] = {
188581362dSIoana Ciornei {DPSW_CNT_ING_FRAME, "[hw] rx frames"},
198581362dSIoana Ciornei {DPSW_CNT_ING_BYTE, "[hw] rx bytes"},
208581362dSIoana Ciornei {DPSW_CNT_ING_FLTR_FRAME, "[hw] rx filtered frames"},
218581362dSIoana Ciornei {DPSW_CNT_ING_FRAME_DISCARD, "[hw] rx discarded frames"},
228581362dSIoana Ciornei {DPSW_CNT_ING_BCAST_FRAME, "[hw] rx bcast frames"},
238581362dSIoana Ciornei {DPSW_CNT_ING_BCAST_BYTES, "[hw] rx bcast bytes"},
248581362dSIoana Ciornei {DPSW_CNT_ING_MCAST_FRAME, "[hw] rx mcast frames"},
258581362dSIoana Ciornei {DPSW_CNT_ING_MCAST_BYTE, "[hw] rx mcast bytes"},
268581362dSIoana Ciornei {DPSW_CNT_EGR_FRAME, "[hw] tx frames"},
278581362dSIoana Ciornei {DPSW_CNT_EGR_BYTE, "[hw] tx bytes"},
288581362dSIoana Ciornei {DPSW_CNT_EGR_FRAME_DISCARD, "[hw] tx discarded frames"},
298581362dSIoana Ciornei {DPSW_CNT_ING_NO_BUFF_DISCARD, "[hw] rx nobuffer discards"},
30b2b3212bSRazvan Stefanescu };
31b2b3212bSRazvan Stefanescu
325ad71958SIoana Ciornei #define DPAA2_SWITCH_NUM_COUNTERS ARRAY_SIZE(dpaa2_switch_ethtool_counters)
33b2b3212bSRazvan Stefanescu
dpaa2_switch_get_drvinfo(struct net_device * netdev,struct ethtool_drvinfo * drvinfo)345ad71958SIoana Ciornei static void dpaa2_switch_get_drvinfo(struct net_device *netdev,
35b2b3212bSRazvan Stefanescu struct ethtool_drvinfo *drvinfo)
36b2b3212bSRazvan Stefanescu {
37b2b3212bSRazvan Stefanescu struct ethsw_port_priv *port_priv = netdev_priv(netdev);
38b2b3212bSRazvan Stefanescu u16 version_major, version_minor;
39b2b3212bSRazvan Stefanescu int err;
40b2b3212bSRazvan Stefanescu
41a288a21eSKumar Kartikeya Dwivedi strscpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
42b2b3212bSRazvan Stefanescu
43b2b3212bSRazvan Stefanescu err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
44b2b3212bSRazvan Stefanescu &version_major,
45b2b3212bSRazvan Stefanescu &version_minor);
46b2b3212bSRazvan Stefanescu if (err)
47a288a21eSKumar Kartikeya Dwivedi strscpy(drvinfo->fw_version, "N/A",
48b2b3212bSRazvan Stefanescu sizeof(drvinfo->fw_version));
49b2b3212bSRazvan Stefanescu else
50b2b3212bSRazvan Stefanescu snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
51b2b3212bSRazvan Stefanescu "%u.%u", version_major, version_minor);
52b2b3212bSRazvan Stefanescu
53a288a21eSKumar Kartikeya Dwivedi strscpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
54b2b3212bSRazvan Stefanescu sizeof(drvinfo->bus_info));
55b2b3212bSRazvan Stefanescu }
56b2b3212bSRazvan Stefanescu
57b2b3212bSRazvan Stefanescu static int
dpaa2_switch_get_link_ksettings(struct net_device * netdev,struct ethtool_link_ksettings * link_ksettings)585ad71958SIoana Ciornei dpaa2_switch_get_link_ksettings(struct net_device *netdev,
59b2b3212bSRazvan Stefanescu struct ethtool_link_ksettings *link_ksettings)
60b2b3212bSRazvan Stefanescu {
61b2b3212bSRazvan Stefanescu struct ethsw_port_priv *port_priv = netdev_priv(netdev);
62b2b3212bSRazvan Stefanescu struct dpsw_link_state state = {0};
633c7f44faSVladimir Oltean int err;
64b2b3212bSRazvan Stefanescu
653c7f44faSVladimir Oltean mutex_lock(&port_priv->mac_lock);
663c7f44faSVladimir Oltean
673c7f44faSVladimir Oltean if (dpaa2_switch_port_is_type_phy(port_priv)) {
683c7f44faSVladimir Oltean err = phylink_ethtool_ksettings_get(port_priv->mac->phylink,
6984cba729SIoana Ciornei link_ksettings);
703c7f44faSVladimir Oltean mutex_unlock(&port_priv->mac_lock);
713c7f44faSVladimir Oltean return err;
723c7f44faSVladimir Oltean }
733c7f44faSVladimir Oltean
743c7f44faSVladimir Oltean mutex_unlock(&port_priv->mac_lock);
7584cba729SIoana Ciornei
76b2b3212bSRazvan Stefanescu err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
77b2b3212bSRazvan Stefanescu port_priv->ethsw_data->dpsw_handle,
78b2b3212bSRazvan Stefanescu port_priv->idx,
79b2b3212bSRazvan Stefanescu &state);
80b2b3212bSRazvan Stefanescu if (err) {
8173866324SIoana Ciornei netdev_err(netdev, "ERROR %d getting link state\n", err);
82b2b3212bSRazvan Stefanescu goto out;
83b2b3212bSRazvan Stefanescu }
84b2b3212bSRazvan Stefanescu
85b2b3212bSRazvan Stefanescu /* At the moment, we have no way of interrogating the DPMAC
86b2b3212bSRazvan Stefanescu * from the DPSW side or there may not exist a DPMAC at all.
87b2b3212bSRazvan Stefanescu * Report only autoneg state, duplexity and speed.
88b2b3212bSRazvan Stefanescu */
89b2b3212bSRazvan Stefanescu if (state.options & DPSW_LINK_OPT_AUTONEG)
90b2b3212bSRazvan Stefanescu link_ksettings->base.autoneg = AUTONEG_ENABLE;
91b2b3212bSRazvan Stefanescu if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX))
92b2b3212bSRazvan Stefanescu link_ksettings->base.duplex = DUPLEX_FULL;
93b2b3212bSRazvan Stefanescu link_ksettings->base.speed = state.rate;
94b2b3212bSRazvan Stefanescu
95b2b3212bSRazvan Stefanescu out:
96b2b3212bSRazvan Stefanescu return err;
97b2b3212bSRazvan Stefanescu }
98b2b3212bSRazvan Stefanescu
99b2b3212bSRazvan Stefanescu static int
dpaa2_switch_set_link_ksettings(struct net_device * netdev,const struct ethtool_link_ksettings * link_ksettings)1005ad71958SIoana Ciornei dpaa2_switch_set_link_ksettings(struct net_device *netdev,
101b2b3212bSRazvan Stefanescu const struct ethtool_link_ksettings *link_ksettings)
102b2b3212bSRazvan Stefanescu {
103b2b3212bSRazvan Stefanescu struct ethsw_port_priv *port_priv = netdev_priv(netdev);
104c391818aSIoana Ciornei struct ethsw_core *ethsw = port_priv->ethsw_data;
105b2b3212bSRazvan Stefanescu struct dpsw_link_cfg cfg = {0};
106c391818aSIoana Ciornei bool if_running;
107c391818aSIoana Ciornei int err = 0, ret;
108b2b3212bSRazvan Stefanescu
1093c7f44faSVladimir Oltean mutex_lock(&port_priv->mac_lock);
1103c7f44faSVladimir Oltean
1113c7f44faSVladimir Oltean if (dpaa2_switch_port_is_type_phy(port_priv)) {
1123c7f44faSVladimir Oltean err = phylink_ethtool_ksettings_set(port_priv->mac->phylink,
11384cba729SIoana Ciornei link_ksettings);
1143c7f44faSVladimir Oltean mutex_unlock(&port_priv->mac_lock);
1153c7f44faSVladimir Oltean return err;
1163c7f44faSVladimir Oltean }
1173c7f44faSVladimir Oltean
1183c7f44faSVladimir Oltean mutex_unlock(&port_priv->mac_lock);
11984cba729SIoana Ciornei
120c391818aSIoana Ciornei /* Interface needs to be down to change link settings */
121c391818aSIoana Ciornei if_running = netif_running(netdev);
122c391818aSIoana Ciornei if (if_running) {
123c391818aSIoana Ciornei err = dpsw_if_disable(ethsw->mc_io, 0,
124c391818aSIoana Ciornei ethsw->dpsw_handle,
125c391818aSIoana Ciornei port_priv->idx);
126c391818aSIoana Ciornei if (err) {
127c391818aSIoana Ciornei netdev_err(netdev, "dpsw_if_disable err %d\n", err);
128c391818aSIoana Ciornei return err;
129c391818aSIoana Ciornei }
130b2b3212bSRazvan Stefanescu }
131b2b3212bSRazvan Stefanescu
132b2b3212bSRazvan Stefanescu cfg.rate = link_ksettings->base.speed;
133b2b3212bSRazvan Stefanescu if (link_ksettings->base.autoneg == AUTONEG_ENABLE)
134b2b3212bSRazvan Stefanescu cfg.options |= DPSW_LINK_OPT_AUTONEG;
135b2b3212bSRazvan Stefanescu else
136b2b3212bSRazvan Stefanescu cfg.options &= ~DPSW_LINK_OPT_AUTONEG;
137b2b3212bSRazvan Stefanescu if (link_ksettings->base.duplex == DUPLEX_HALF)
138b2b3212bSRazvan Stefanescu cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX;
139b2b3212bSRazvan Stefanescu else
140b2b3212bSRazvan Stefanescu cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX;
141b2b3212bSRazvan Stefanescu
142b2b3212bSRazvan Stefanescu err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0,
143b2b3212bSRazvan Stefanescu port_priv->ethsw_data->dpsw_handle,
144b2b3212bSRazvan Stefanescu port_priv->idx,
145b2b3212bSRazvan Stefanescu &cfg);
146b2b3212bSRazvan Stefanescu
147c391818aSIoana Ciornei if (if_running) {
148c391818aSIoana Ciornei ret = dpsw_if_enable(ethsw->mc_io, 0,
149c391818aSIoana Ciornei ethsw->dpsw_handle,
150c391818aSIoana Ciornei port_priv->idx);
151c391818aSIoana Ciornei if (ret) {
152c391818aSIoana Ciornei netdev_err(netdev, "dpsw_if_enable err %d\n", ret);
153c391818aSIoana Ciornei return ret;
154c391818aSIoana Ciornei }
155c391818aSIoana Ciornei }
156b2b3212bSRazvan Stefanescu return err;
157b2b3212bSRazvan Stefanescu }
158b2b3212bSRazvan Stefanescu
159f0653a89SIoana Ciornei static int
dpaa2_switch_ethtool_get_sset_count(struct net_device * netdev,int sset)160f0653a89SIoana Ciornei dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset)
161b2b3212bSRazvan Stefanescu {
162b2b3212bSRazvan Stefanescu switch (sset) {
163b2b3212bSRazvan Stefanescu case ETH_SS_STATS:
16429811d6eSVladimir Oltean return DPAA2_SWITCH_NUM_COUNTERS + dpaa2_mac_get_sset_count();
165b2b3212bSRazvan Stefanescu default:
166b2b3212bSRazvan Stefanescu return -EOPNOTSUPP;
167b2b3212bSRazvan Stefanescu }
168b2b3212bSRazvan Stefanescu }
169b2b3212bSRazvan Stefanescu
dpaa2_switch_ethtool_get_strings(struct net_device * netdev,u32 stringset,u8 * data)1705ad71958SIoana Ciornei static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev,
171b2b3212bSRazvan Stefanescu u32 stringset, u8 *data)
172b2b3212bSRazvan Stefanescu {
173*f611cc38SRosen Penev const char *str;
174b2b3212bSRazvan Stefanescu int i;
175b2b3212bSRazvan Stefanescu
176b2b3212bSRazvan Stefanescu switch (stringset) {
177b2b3212bSRazvan Stefanescu case ETH_SS_STATS:
178f0653a89SIoana Ciornei for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) {
179*f611cc38SRosen Penev str = dpaa2_switch_ethtool_counters[i].name;
180*f611cc38SRosen Penev ethtool_puts(&data, str);
181f0653a89SIoana Ciornei }
182*f611cc38SRosen Penev dpaa2_mac_get_strings(&data);
183b2b3212bSRazvan Stefanescu break;
184b2b3212bSRazvan Stefanescu }
185b2b3212bSRazvan Stefanescu }
186b2b3212bSRazvan Stefanescu
dpaa2_switch_ethtool_get_stats(struct net_device * netdev,struct ethtool_stats * stats,u64 * data)1875ad71958SIoana Ciornei static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev,
188b2b3212bSRazvan Stefanescu struct ethtool_stats *stats,
189b2b3212bSRazvan Stefanescu u64 *data)
190b2b3212bSRazvan Stefanescu {
191b2b3212bSRazvan Stefanescu struct ethsw_port_priv *port_priv = netdev_priv(netdev);
192b2b3212bSRazvan Stefanescu int i, err;
193b2b3212bSRazvan Stefanescu
1945ad71958SIoana Ciornei for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) {
195b2b3212bSRazvan Stefanescu err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
196b2b3212bSRazvan Stefanescu port_priv->ethsw_data->dpsw_handle,
197b2b3212bSRazvan Stefanescu port_priv->idx,
1985ad71958SIoana Ciornei dpaa2_switch_ethtool_counters[i].id,
199b2b3212bSRazvan Stefanescu &data[i]);
200b2b3212bSRazvan Stefanescu if (err)
201b2b3212bSRazvan Stefanescu netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n",
2025ad71958SIoana Ciornei dpaa2_switch_ethtool_counters[i].name, err);
203b2b3212bSRazvan Stefanescu }
204f0653a89SIoana Ciornei
2053c7f44faSVladimir Oltean mutex_lock(&port_priv->mac_lock);
2063c7f44faSVladimir Oltean
207bc230671SVladimir Oltean if (dpaa2_switch_port_has_mac(port_priv))
208f0653a89SIoana Ciornei dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i);
2093c7f44faSVladimir Oltean
2103c7f44faSVladimir Oltean mutex_unlock(&port_priv->mac_lock);
211b2b3212bSRazvan Stefanescu }
212b2b3212bSRazvan Stefanescu
2135ad71958SIoana Ciornei const struct ethtool_ops dpaa2_switch_port_ethtool_ops = {
2145ad71958SIoana Ciornei .get_drvinfo = dpaa2_switch_get_drvinfo,
215b2b3212bSRazvan Stefanescu .get_link = ethtool_op_get_link,
2165ad71958SIoana Ciornei .get_link_ksettings = dpaa2_switch_get_link_ksettings,
2175ad71958SIoana Ciornei .set_link_ksettings = dpaa2_switch_set_link_ksettings,
2185ad71958SIoana Ciornei .get_strings = dpaa2_switch_ethtool_get_strings,
2195ad71958SIoana Ciornei .get_ethtool_stats = dpaa2_switch_ethtool_get_stats,
2205ad71958SIoana Ciornei .get_sset_count = dpaa2_switch_ethtool_get_sset_count,
221b2b3212bSRazvan Stefanescu };
222