Commit | Line | Data |
---|---|---|
dcc4086d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
786eec27 TI |
2 | /* |
3 | * FUJITSU Extended Socket Network Device driver | |
4 | * Copyright (c) 2015 FUJITSU LIMITED | |
786eec27 TI |
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, \ | |
c593642c | 24 | .sizeof_stat = sizeof_field(struct fjes_adapter, stat), \ |
786eec27 TI |
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 | ||
21b7efbc TI |
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 | ||
786eec27 TI |
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); | |
21b7efbc TI |
47 | struct fjes_hw *hw = &adapter->hw; |
48 | int epidx; | |
786eec27 TI |
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 | } | |
21b7efbc TI |
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 | } | |
786eec27 TI |
83 | } |
84 | ||
85 | static void fjes_get_strings(struct net_device *netdev, | |
86 | u32 stringset, u8 *data) | |
87 | { | |
21b7efbc TI |
88 | struct fjes_adapter *adapter = netdev_priv(netdev); |
89 | struct fjes_hw *hw = &adapter->hw; | |
786eec27 TI |
90 | u8 *p = data; |
91 | int i; | |
92 | ||
93 | switch (stringset) { | |
94 | case ETH_SS_STATS: | |
95 | for (i = 0; i < ARRAY_SIZE(fjes_gstrings_stats); i++) { | |
96 | memcpy(p, fjes_gstrings_stats[i].stat_string, | |
97 | ETH_GSTRING_LEN); | |
98 | p += ETH_GSTRING_LEN; | |
99 | } | |
21b7efbc TI |
100 | for (i = 0; i < hw->max_epid; i++) { |
101 | if (i == hw->my_epid) | |
102 | continue; | |
103 | sprintf(p, "ep%u_com_regist_buf_exec", i); | |
104 | p += ETH_GSTRING_LEN; | |
105 | sprintf(p, "ep%u_com_unregist_buf_exec", i); | |
106 | p += ETH_GSTRING_LEN; | |
107 | sprintf(p, "ep%u_send_intr_rx", i); | |
108 | p += ETH_GSTRING_LEN; | |
109 | sprintf(p, "ep%u_send_intr_unshare", i); | |
110 | p += ETH_GSTRING_LEN; | |
111 | sprintf(p, "ep%u_send_intr_zoneupdate", i); | |
112 | p += ETH_GSTRING_LEN; | |
113 | sprintf(p, "ep%u_recv_intr_rx", i); | |
114 | p += ETH_GSTRING_LEN; | |
115 | sprintf(p, "ep%u_recv_intr_unshare", i); | |
116 | p += ETH_GSTRING_LEN; | |
117 | sprintf(p, "ep%u_recv_intr_stop", i); | |
118 | p += ETH_GSTRING_LEN; | |
119 | sprintf(p, "ep%u_recv_intr_zoneupdate", i); | |
120 | p += ETH_GSTRING_LEN; | |
121 | sprintf(p, "ep%u_tx_buffer_full", i); | |
122 | p += ETH_GSTRING_LEN; | |
123 | sprintf(p, "ep%u_tx_dropped_not_shared", i); | |
124 | p += ETH_GSTRING_LEN; | |
125 | sprintf(p, "ep%u_tx_dropped_ver_mismatch", i); | |
126 | p += ETH_GSTRING_LEN; | |
127 | sprintf(p, "ep%u_tx_dropped_buf_size_mismatch", i); | |
128 | p += ETH_GSTRING_LEN; | |
129 | sprintf(p, "ep%u_tx_dropped_vlanid_mismatch", i); | |
130 | p += ETH_GSTRING_LEN; | |
131 | } | |
786eec27 TI |
132 | break; |
133 | } | |
134 | } | |
135 | ||
136 | static int fjes_get_sset_count(struct net_device *netdev, int sset) | |
137 | { | |
138 | switch (sset) { | |
139 | case ETH_SS_STATS: | |
21b7efbc | 140 | return FJES_STATS_LEN; |
786eec27 TI |
141 | default: |
142 | return -EOPNOTSUPP; | |
143 | } | |
144 | } | |
145 | ||
146 | static void fjes_get_drvinfo(struct net_device *netdev, | |
147 | struct ethtool_drvinfo *drvinfo) | |
148 | { | |
149 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
150 | struct platform_device *plat_dev; | |
151 | ||
152 | plat_dev = adapter->plat_dev; | |
153 | ||
154 | strlcpy(drvinfo->driver, fjes_driver_name, sizeof(drvinfo->driver)); | |
155 | strlcpy(drvinfo->version, fjes_driver_version, | |
156 | sizeof(drvinfo->version)); | |
157 | ||
158 | strlcpy(drvinfo->fw_version, "none", sizeof(drvinfo->fw_version)); | |
159 | snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info), | |
160 | "platform:%s", plat_dev->name); | |
786eec27 TI |
161 | } |
162 | ||
031e5144 PR |
163 | static int fjes_get_link_ksettings(struct net_device *netdev, |
164 | struct ethtool_link_ksettings *ecmd) | |
786eec27 | 165 | { |
031e5144 PR |
166 | ethtool_link_ksettings_zero_link_mode(ecmd, supported); |
167 | ethtool_link_ksettings_zero_link_mode(ecmd, advertising); | |
168 | ecmd->base.duplex = DUPLEX_FULL; | |
169 | ecmd->base.autoneg = AUTONEG_DISABLE; | |
170 | ecmd->base.port = PORT_NONE; | |
171 | ecmd->base.speed = 20000; /* 20Gb/s */ | |
786eec27 TI |
172 | |
173 | return 0; | |
174 | } | |
175 | ||
462d8074 TI |
176 | static int fjes_get_regs_len(struct net_device *netdev) |
177 | { | |
178 | #define FJES_REGS_LEN 37 | |
179 | return FJES_REGS_LEN * sizeof(u32); | |
180 | } | |
181 | ||
182 | static void fjes_get_regs(struct net_device *netdev, | |
183 | struct ethtool_regs *regs, void *p) | |
184 | { | |
185 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
186 | struct fjes_hw *hw = &adapter->hw; | |
187 | u32 *regs_buff = p; | |
188 | ||
189 | memset(p, 0, FJES_REGS_LEN * sizeof(u32)); | |
190 | ||
191 | regs->version = 1; | |
192 | ||
193 | /* Information registers */ | |
194 | regs_buff[0] = rd32(XSCT_OWNER_EPID); | |
195 | regs_buff[1] = rd32(XSCT_MAX_EP); | |
196 | ||
197 | /* Device Control registers */ | |
198 | regs_buff[4] = rd32(XSCT_DCTL); | |
199 | ||
200 | /* Command Control registers */ | |
201 | regs_buff[8] = rd32(XSCT_CR); | |
202 | regs_buff[9] = rd32(XSCT_CS); | |
203 | regs_buff[10] = rd32(XSCT_SHSTSAL); | |
204 | regs_buff[11] = rd32(XSCT_SHSTSAH); | |
205 | ||
206 | regs_buff[13] = rd32(XSCT_REQBL); | |
207 | regs_buff[14] = rd32(XSCT_REQBAL); | |
208 | regs_buff[15] = rd32(XSCT_REQBAH); | |
209 | ||
210 | regs_buff[17] = rd32(XSCT_RESPBL); | |
211 | regs_buff[18] = rd32(XSCT_RESPBAL); | |
212 | regs_buff[19] = rd32(XSCT_RESPBAH); | |
213 | ||
214 | /* Interrupt Control registers */ | |
215 | regs_buff[32] = rd32(XSCT_IS); | |
216 | regs_buff[33] = rd32(XSCT_IMS); | |
217 | regs_buff[34] = rd32(XSCT_IMC); | |
218 | regs_buff[35] = rd32(XSCT_IG); | |
219 | regs_buff[36] = rd32(XSCT_ICTL); | |
220 | } | |
221 | ||
b6ba737d TI |
222 | static int fjes_set_dump(struct net_device *netdev, struct ethtool_dump *dump) |
223 | { | |
224 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
225 | struct fjes_hw *hw = &adapter->hw; | |
226 | int ret = 0; | |
227 | ||
228 | if (dump->flag) { | |
229 | if (hw->debug_mode) | |
230 | return -EPERM; | |
231 | ||
232 | hw->debug_mode = dump->flag; | |
233 | ||
234 | /* enable debug mode */ | |
235 | mutex_lock(&hw->hw_info.lock); | |
236 | ret = fjes_hw_start_debug(hw); | |
237 | mutex_unlock(&hw->hw_info.lock); | |
238 | ||
239 | if (ret) | |
240 | hw->debug_mode = 0; | |
241 | } else { | |
242 | if (!hw->debug_mode) | |
243 | return -EPERM; | |
244 | ||
245 | /* disable debug mode */ | |
246 | mutex_lock(&hw->hw_info.lock); | |
247 | ret = fjes_hw_stop_debug(hw); | |
248 | mutex_unlock(&hw->hw_info.lock); | |
249 | } | |
250 | ||
251 | return ret; | |
252 | } | |
253 | ||
254 | static int fjes_get_dump_flag(struct net_device *netdev, | |
255 | struct ethtool_dump *dump) | |
256 | { | |
257 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
258 | struct fjes_hw *hw = &adapter->hw; | |
259 | ||
260 | dump->len = hw->hw_info.trace_size; | |
261 | dump->version = 1; | |
262 | dump->flag = hw->debug_mode; | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
267 | static int fjes_get_dump_data(struct net_device *netdev, | |
268 | struct ethtool_dump *dump, void *buf) | |
269 | { | |
270 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
271 | struct fjes_hw *hw = &adapter->hw; | |
272 | int ret = 0; | |
273 | ||
274 | if (hw->hw_info.trace) | |
275 | memcpy(buf, hw->hw_info.trace, hw->hw_info.trace_size); | |
276 | else | |
277 | ret = -EPERM; | |
278 | ||
279 | return ret; | |
280 | } | |
281 | ||
786eec27 | 282 | static const struct ethtool_ops fjes_ethtool_ops = { |
786eec27 TI |
283 | .get_drvinfo = fjes_get_drvinfo, |
284 | .get_ethtool_stats = fjes_get_ethtool_stats, | |
285 | .get_strings = fjes_get_strings, | |
286 | .get_sset_count = fjes_get_sset_count, | |
462d8074 TI |
287 | .get_regs = fjes_get_regs, |
288 | .get_regs_len = fjes_get_regs_len, | |
b6ba737d TI |
289 | .set_dump = fjes_set_dump, |
290 | .get_dump_flag = fjes_get_dump_flag, | |
291 | .get_dump_data = fjes_get_dump_data, | |
031e5144 | 292 | .get_link_ksettings = fjes_get_link_ksettings, |
786eec27 TI |
293 | }; |
294 | ||
295 | void fjes_set_ethtool_ops(struct net_device *netdev) | |
296 | { | |
297 | netdev->ethtool_ops = &fjes_ethtool_ops; | |
298 | } |