1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * DPAA2 Ethernet Switch ethtool support
4 *
5 * Copyright 2014-2016 Freescale Semiconductor Inc.
6 * Copyright 2017-2018 NXP
7 *
8 */
9
10 #include "ethsw.h"
11
12 static struct {
13 enum dpsw_counter id;
14 char name[ETH_GSTRING_LEN];
15 } dpaa2_switch_ethtool_counters[] = {
16 {DPSW_CNT_ING_FRAME, "rx frames"},
17 {DPSW_CNT_ING_BYTE, "rx bytes"},
18 {DPSW_CNT_ING_FLTR_FRAME, "rx filtered frames"},
19 {DPSW_CNT_ING_FRAME_DISCARD, "rx discarded frames"},
20 {DPSW_CNT_ING_BCAST_FRAME, "rx b-cast frames"},
21 {DPSW_CNT_ING_BCAST_BYTES, "rx b-cast bytes"},
22 {DPSW_CNT_ING_MCAST_FRAME, "rx m-cast frames"},
23 {DPSW_CNT_ING_MCAST_BYTE, "rx m-cast bytes"},
24 {DPSW_CNT_EGR_FRAME, "tx frames"},
25 {DPSW_CNT_EGR_BYTE, "tx bytes"},
26 {DPSW_CNT_EGR_FRAME_DISCARD, "tx discarded frames"},
27
28 };
29
30 #define DPAA2_SWITCH_NUM_COUNTERS ARRAY_SIZE(dpaa2_switch_ethtool_counters)
31
dpaa2_switch_get_drvinfo(struct net_device * netdev,struct ethtool_drvinfo * drvinfo)32 static void dpaa2_switch_get_drvinfo(struct net_device *netdev,
33 struct ethtool_drvinfo *drvinfo)
34 {
35 struct ethsw_port_priv *port_priv = netdev_priv(netdev);
36 u16 version_major, version_minor;
37 int err;
38
39 strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver));
40
41 err = dpsw_get_api_version(port_priv->ethsw_data->mc_io, 0,
42 &version_major,
43 &version_minor);
44 if (err)
45 strlcpy(drvinfo->fw_version, "N/A",
46 sizeof(drvinfo->fw_version));
47 else
48 snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
49 "%u.%u", version_major, version_minor);
50
51 strlcpy(drvinfo->bus_info, dev_name(netdev->dev.parent->parent),
52 sizeof(drvinfo->bus_info));
53 }
54
55 static int
dpaa2_switch_get_link_ksettings(struct net_device * netdev,struct ethtool_link_ksettings * link_ksettings)56 dpaa2_switch_get_link_ksettings(struct net_device *netdev,
57 struct ethtool_link_ksettings *link_ksettings)
58 {
59 struct ethsw_port_priv *port_priv = netdev_priv(netdev);
60 struct dpsw_link_state state = {0};
61 int err = 0;
62
63 err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
64 port_priv->ethsw_data->dpsw_handle,
65 port_priv->idx,
66 &state);
67 if (err) {
68 netdev_err(netdev, "ERROR %d getting link state\n", err);
69 goto out;
70 }
71
72 /* At the moment, we have no way of interrogating the DPMAC
73 * from the DPSW side or there may not exist a DPMAC at all.
74 * Report only autoneg state, duplexity and speed.
75 */
76 if (state.options & DPSW_LINK_OPT_AUTONEG)
77 link_ksettings->base.autoneg = AUTONEG_ENABLE;
78 if (!(state.options & DPSW_LINK_OPT_HALF_DUPLEX))
79 link_ksettings->base.duplex = DUPLEX_FULL;
80 link_ksettings->base.speed = state.rate;
81
82 out:
83 return err;
84 }
85
86 static int
dpaa2_switch_set_link_ksettings(struct net_device * netdev,const struct ethtool_link_ksettings * link_ksettings)87 dpaa2_switch_set_link_ksettings(struct net_device *netdev,
88 const struct ethtool_link_ksettings *link_ksettings)
89 {
90 struct ethsw_port_priv *port_priv = netdev_priv(netdev);
91 struct ethsw_core *ethsw = port_priv->ethsw_data;
92 struct dpsw_link_cfg cfg = {0};
93 bool if_running;
94 int err = 0, ret;
95
96 /* Interface needs to be down to change link settings */
97 if_running = netif_running(netdev);
98 if (if_running) {
99 err = dpsw_if_disable(ethsw->mc_io, 0,
100 ethsw->dpsw_handle,
101 port_priv->idx);
102 if (err) {
103 netdev_err(netdev, "dpsw_if_disable err %d\n", err);
104 return err;
105 }
106 }
107
108 cfg.rate = link_ksettings->base.speed;
109 if (link_ksettings->base.autoneg == AUTONEG_ENABLE)
110 cfg.options |= DPSW_LINK_OPT_AUTONEG;
111 else
112 cfg.options &= ~DPSW_LINK_OPT_AUTONEG;
113 if (link_ksettings->base.duplex == DUPLEX_HALF)
114 cfg.options |= DPSW_LINK_OPT_HALF_DUPLEX;
115 else
116 cfg.options &= ~DPSW_LINK_OPT_HALF_DUPLEX;
117
118 err = dpsw_if_set_link_cfg(port_priv->ethsw_data->mc_io, 0,
119 port_priv->ethsw_data->dpsw_handle,
120 port_priv->idx,
121 &cfg);
122
123 if (if_running) {
124 ret = dpsw_if_enable(ethsw->mc_io, 0,
125 ethsw->dpsw_handle,
126 port_priv->idx);
127 if (ret) {
128 netdev_err(netdev, "dpsw_if_enable err %d\n", ret);
129 return ret;
130 }
131 }
132 return err;
133 }
134
dpaa2_switch_ethtool_get_sset_count(struct net_device * dev,int sset)135 static int dpaa2_switch_ethtool_get_sset_count(struct net_device *dev, int sset)
136 {
137 switch (sset) {
138 case ETH_SS_STATS:
139 return DPAA2_SWITCH_NUM_COUNTERS;
140 default:
141 return -EOPNOTSUPP;
142 }
143 }
144
dpaa2_switch_ethtool_get_strings(struct net_device * netdev,u32 stringset,u8 * data)145 static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev,
146 u32 stringset, u8 *data)
147 {
148 int i;
149
150 switch (stringset) {
151 case ETH_SS_STATS:
152 for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++)
153 memcpy(data + i * ETH_GSTRING_LEN,
154 dpaa2_switch_ethtool_counters[i].name,
155 ETH_GSTRING_LEN);
156 break;
157 }
158 }
159
dpaa2_switch_ethtool_get_stats(struct net_device * netdev,struct ethtool_stats * stats,u64 * data)160 static void dpaa2_switch_ethtool_get_stats(struct net_device *netdev,
161 struct ethtool_stats *stats,
162 u64 *data)
163 {
164 struct ethsw_port_priv *port_priv = netdev_priv(netdev);
165 int i, err;
166
167 for (i = 0; i < DPAA2_SWITCH_NUM_COUNTERS; i++) {
168 err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
169 port_priv->ethsw_data->dpsw_handle,
170 port_priv->idx,
171 dpaa2_switch_ethtool_counters[i].id,
172 &data[i]);
173 if (err)
174 netdev_err(netdev, "dpsw_if_get_counter[%s] err %d\n",
175 dpaa2_switch_ethtool_counters[i].name, err);
176 }
177 }
178
179 const struct ethtool_ops dpaa2_switch_port_ethtool_ops = {
180 .get_drvinfo = dpaa2_switch_get_drvinfo,
181 .get_link = ethtool_op_get_link,
182 .get_link_ksettings = dpaa2_switch_get_link_ksettings,
183 .set_link_ksettings = dpaa2_switch_set_link_ksettings,
184 .get_strings = dpaa2_switch_ethtool_get_strings,
185 .get_ethtool_stats = dpaa2_switch_ethtool_get_stats,
186 .get_sset_count = dpaa2_switch_ethtool_get_sset_count,
187 };
188