xref: /linux/drivers/net/fjes/fjes_ethtool.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  FUJITSU Extended Socket Network Device driver
4  *  Copyright (c) 2015 FUJITSU LIMITED
5  */
6 
7 /* ethtool support for fjes */
8 
9 #include <linux/vmalloc.h>
10 #include <linux/netdevice.h>
11 #include <linux/ethtool.h>
12 #include <linux/platform_device.h>
13 
14 #include "fjes.h"
15 
16 struct fjes_stats {
17 	char stat_string[ETH_GSTRING_LEN];
18 	int sizeof_stat;
19 	int stat_offset;
20 };
21 
22 #define FJES_STAT(name, stat) { \
23 	.stat_string = name, \
24 	.sizeof_stat = sizeof_field(struct fjes_adapter, stat), \
25 	.stat_offset = offsetof(struct fjes_adapter, stat) \
26 }
27 
28 static const struct fjes_stats fjes_gstrings_stats[] = {
29 	FJES_STAT("rx_packets", stats64.rx_packets),
30 	FJES_STAT("tx_packets", stats64.tx_packets),
31 	FJES_STAT("rx_bytes", stats64.rx_bytes),
32 	FJES_STAT("tx_bytes", stats64.rx_bytes),
33 	FJES_STAT("rx_dropped", stats64.rx_dropped),
34 	FJES_STAT("tx_dropped", stats64.tx_dropped),
35 };
36 
37 #define FJES_EP_STATS_LEN 14
38 #define FJES_STATS_LEN \
39 	(ARRAY_SIZE(fjes_gstrings_stats) + \
40 	 ((&((struct fjes_adapter *)netdev_priv(netdev))->hw)->max_epid - 1) * \
41 	 FJES_EP_STATS_LEN)
42 
43 static void fjes_get_ethtool_stats(struct net_device *netdev,
44 				   struct ethtool_stats *stats, u64 *data)
45 {
46 	struct fjes_adapter *adapter = netdev_priv(netdev);
47 	struct fjes_hw *hw = &adapter->hw;
48 	int epidx;
49 	char *p;
50 	int i;
51 
52 	for (i = 0; i < ARRAY_SIZE(fjes_gstrings_stats); i++) {
53 		p = (char *)adapter + fjes_gstrings_stats[i].stat_offset;
54 		data[i] = (fjes_gstrings_stats[i].sizeof_stat == sizeof(u64))
55 			? *(u64 *)p : *(u32 *)p;
56 	}
57 	for (epidx = 0; epidx < hw->max_epid; epidx++) {
58 		if (epidx == hw->my_epid)
59 			continue;
60 		data[i++] = hw->ep_shm_info[epidx].ep_stats
61 				.com_regist_buf_exec;
62 		data[i++] = hw->ep_shm_info[epidx].ep_stats
63 				.com_unregist_buf_exec;
64 		data[i++] = hw->ep_shm_info[epidx].ep_stats.send_intr_rx;
65 		data[i++] = hw->ep_shm_info[epidx].ep_stats.send_intr_unshare;
66 		data[i++] = hw->ep_shm_info[epidx].ep_stats
67 				.send_intr_zoneupdate;
68 		data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_rx;
69 		data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_unshare;
70 		data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_stop;
71 		data[i++] = hw->ep_shm_info[epidx].ep_stats
72 				.recv_intr_zoneupdate;
73 		data[i++] = hw->ep_shm_info[epidx].ep_stats.tx_buffer_full;
74 		data[i++] = hw->ep_shm_info[epidx].ep_stats
75 				.tx_dropped_not_shared;
76 		data[i++] = hw->ep_shm_info[epidx].ep_stats
77 				.tx_dropped_ver_mismatch;
78 		data[i++] = hw->ep_shm_info[epidx].ep_stats
79 				.tx_dropped_buf_size_mismatch;
80 		data[i++] = hw->ep_shm_info[epidx].ep_stats
81 				.tx_dropped_vlanid_mismatch;
82 	}
83 }
84 
85 static void fjes_get_strings(struct net_device *netdev,
86 			     u32 stringset, u8 *data)
87 {
88 	struct fjes_adapter *adapter = netdev_priv(netdev);
89 	struct fjes_hw *hw = &adapter->hw;
90 	int i;
91 
92 	if (stringset != ETH_SS_STATS)
93 		return;
94 
95 	for (i = 0; i < ARRAY_SIZE(fjes_gstrings_stats); i++)
96 		ethtool_puts(&data, fjes_gstrings_stats[i].stat_string);
97 
98 	for (i = 0; i < hw->max_epid; i++) {
99 		if (i == hw->my_epid)
100 			continue;
101 		ethtool_sprintf(&data, "ep%u_com_regist_buf_exec", i);
102 		ethtool_sprintf(&data, "ep%u_com_unregist_buf_exec", i);
103 		ethtool_sprintf(&data, "ep%u_send_intr_rx", i);
104 		ethtool_sprintf(&data, "ep%u_send_intr_unshare", i);
105 		ethtool_sprintf(&data, "ep%u_send_intr_zoneupdate", i);
106 		ethtool_sprintf(&data, "ep%u_recv_intr_rx", i);
107 		ethtool_sprintf(&data, "ep%u_recv_intr_unshare", i);
108 		ethtool_sprintf(&data, "ep%u_recv_intr_stop", i);
109 		ethtool_sprintf(&data, "ep%u_recv_intr_zoneupdate", i);
110 		ethtool_sprintf(&data, "ep%u_tx_buffer_full", i);
111 		ethtool_sprintf(&data, "ep%u_tx_dropped_not_shared", i);
112 		ethtool_sprintf(&data, "ep%u_tx_dropped_ver_mismatch", i);
113 		ethtool_sprintf(&data, "ep%u_tx_dropped_buf_size_mismatch", i);
114 		ethtool_sprintf(&data, "ep%u_tx_dropped_vlanid_mismatch", i);
115 	}
116 }
117 
118 static int fjes_get_sset_count(struct net_device *netdev, int sset)
119 {
120 	switch (sset) {
121 	case ETH_SS_STATS:
122 		return FJES_STATS_LEN;
123 	default:
124 		return -EOPNOTSUPP;
125 	}
126 }
127 
128 static void fjes_get_drvinfo(struct net_device *netdev,
129 			     struct ethtool_drvinfo *drvinfo)
130 {
131 	struct fjes_adapter *adapter = netdev_priv(netdev);
132 	struct platform_device *plat_dev;
133 
134 	plat_dev = adapter->plat_dev;
135 
136 	strscpy(drvinfo->driver, fjes_driver_name, sizeof(drvinfo->driver));
137 	strscpy(drvinfo->version, fjes_driver_version,
138 		sizeof(drvinfo->version));
139 
140 	strscpy(drvinfo->fw_version, "none", sizeof(drvinfo->fw_version));
141 	snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info),
142 		 "platform:%s", plat_dev->name);
143 }
144 
145 static int fjes_get_link_ksettings(struct net_device *netdev,
146 				   struct ethtool_link_ksettings *ecmd)
147 {
148 	ethtool_link_ksettings_zero_link_mode(ecmd, supported);
149 	ethtool_link_ksettings_zero_link_mode(ecmd, advertising);
150 	ecmd->base.duplex = DUPLEX_FULL;
151 	ecmd->base.autoneg = AUTONEG_DISABLE;
152 	ecmd->base.port = PORT_NONE;
153 	ecmd->base.speed = 20000;	/* 20Gb/s */
154 
155 	return 0;
156 }
157 
158 static int fjes_get_regs_len(struct net_device *netdev)
159 {
160 #define FJES_REGS_LEN	37
161 	return FJES_REGS_LEN * sizeof(u32);
162 }
163 
164 static void fjes_get_regs(struct net_device *netdev,
165 			  struct ethtool_regs *regs, void *p)
166 {
167 	struct fjes_adapter *adapter = netdev_priv(netdev);
168 	struct fjes_hw *hw = &adapter->hw;
169 	u32 *regs_buff = p;
170 
171 	memset(p, 0, FJES_REGS_LEN * sizeof(u32));
172 
173 	regs->version = 1;
174 
175 	/* Information registers */
176 	regs_buff[0] = rd32(XSCT_OWNER_EPID);
177 	regs_buff[1] = rd32(XSCT_MAX_EP);
178 
179 	/* Device Control registers */
180 	regs_buff[4] = rd32(XSCT_DCTL);
181 
182 	/* Command Control registers */
183 	regs_buff[8] = rd32(XSCT_CR);
184 	regs_buff[9] = rd32(XSCT_CS);
185 	regs_buff[10] = rd32(XSCT_SHSTSAL);
186 	regs_buff[11] = rd32(XSCT_SHSTSAH);
187 
188 	regs_buff[13] = rd32(XSCT_REQBL);
189 	regs_buff[14] = rd32(XSCT_REQBAL);
190 	regs_buff[15] = rd32(XSCT_REQBAH);
191 
192 	regs_buff[17] = rd32(XSCT_RESPBL);
193 	regs_buff[18] = rd32(XSCT_RESPBAL);
194 	regs_buff[19] = rd32(XSCT_RESPBAH);
195 
196 	/* Interrupt Control registers */
197 	regs_buff[32] = rd32(XSCT_IS);
198 	regs_buff[33] = rd32(XSCT_IMS);
199 	regs_buff[34] = rd32(XSCT_IMC);
200 	regs_buff[35] = rd32(XSCT_IG);
201 	regs_buff[36] = rd32(XSCT_ICTL);
202 }
203 
204 static int fjes_set_dump(struct net_device *netdev, struct ethtool_dump *dump)
205 {
206 	struct fjes_adapter *adapter = netdev_priv(netdev);
207 	struct fjes_hw *hw = &adapter->hw;
208 	int ret = 0;
209 
210 	if (dump->flag) {
211 		if (hw->debug_mode)
212 			return -EPERM;
213 
214 		hw->debug_mode = dump->flag;
215 
216 		/* enable debug mode */
217 		mutex_lock(&hw->hw_info.lock);
218 		ret = fjes_hw_start_debug(hw);
219 		mutex_unlock(&hw->hw_info.lock);
220 
221 		if (ret)
222 			hw->debug_mode = 0;
223 	} else {
224 		if (!hw->debug_mode)
225 			return -EPERM;
226 
227 		/* disable debug mode */
228 		mutex_lock(&hw->hw_info.lock);
229 		ret = fjes_hw_stop_debug(hw);
230 		mutex_unlock(&hw->hw_info.lock);
231 	}
232 
233 	return ret;
234 }
235 
236 static int fjes_get_dump_flag(struct net_device *netdev,
237 			      struct ethtool_dump *dump)
238 {
239 	struct fjes_adapter *adapter = netdev_priv(netdev);
240 	struct fjes_hw *hw = &adapter->hw;
241 
242 	dump->len = hw->hw_info.trace_size;
243 	dump->version = 1;
244 	dump->flag = hw->debug_mode;
245 
246 	return 0;
247 }
248 
249 static int fjes_get_dump_data(struct net_device *netdev,
250 			      struct ethtool_dump *dump, void *buf)
251 {
252 	struct fjes_adapter *adapter = netdev_priv(netdev);
253 	struct fjes_hw *hw = &adapter->hw;
254 	int ret = 0;
255 
256 	if (hw->hw_info.trace)
257 		memcpy(buf, hw->hw_info.trace, hw->hw_info.trace_size);
258 	else
259 		ret = -EPERM;
260 
261 	return ret;
262 }
263 
264 static const struct ethtool_ops fjes_ethtool_ops = {
265 		.get_drvinfo		= fjes_get_drvinfo,
266 		.get_ethtool_stats = fjes_get_ethtool_stats,
267 		.get_strings      = fjes_get_strings,
268 		.get_sset_count   = fjes_get_sset_count,
269 		.get_regs		= fjes_get_regs,
270 		.get_regs_len		= fjes_get_regs_len,
271 		.set_dump		= fjes_set_dump,
272 		.get_dump_flag		= fjes_get_dump_flag,
273 		.get_dump_data		= fjes_get_dump_data,
274 		.get_link_ksettings	= fjes_get_link_ksettings,
275 };
276 
277 void fjes_set_ethtool_ops(struct net_device *netdev)
278 {
279 	netdev->ethtool_ops = &fjes_ethtool_ops;
280 }
281