Commit | Line | Data |
---|---|---|
786eec27 TI |
1 | /* |
2 | * FUJITSU Extended Socket Network Device driver | |
3 | * Copyright (c) 2015 FUJITSU LIMITED | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms and conditions of the GNU General Public License, | |
7 | * version 2, as published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program; if not, see <http://www.gnu.org/licenses/>. | |
16 | * | |
17 | * The full GNU General Public License is included in this distribution in | |
18 | * the file called "COPYING". | |
19 | * | |
20 | */ | |
21 | ||
22 | /* ethtool support for fjes */ | |
23 | ||
24 | #include <linux/vmalloc.h> | |
25 | #include <linux/netdevice.h> | |
26 | #include <linux/ethtool.h> | |
27 | #include <linux/platform_device.h> | |
28 | ||
29 | #include "fjes.h" | |
30 | ||
31 | struct fjes_stats { | |
32 | char stat_string[ETH_GSTRING_LEN]; | |
33 | int sizeof_stat; | |
34 | int stat_offset; | |
35 | }; | |
36 | ||
37 | #define FJES_STAT(name, stat) { \ | |
38 | .stat_string = name, \ | |
39 | .sizeof_stat = FIELD_SIZEOF(struct fjes_adapter, stat), \ | |
40 | .stat_offset = offsetof(struct fjes_adapter, stat) \ | |
41 | } | |
42 | ||
43 | static const struct fjes_stats fjes_gstrings_stats[] = { | |
44 | FJES_STAT("rx_packets", stats64.rx_packets), | |
45 | FJES_STAT("tx_packets", stats64.tx_packets), | |
46 | FJES_STAT("rx_bytes", stats64.rx_bytes), | |
47 | FJES_STAT("tx_bytes", stats64.rx_bytes), | |
48 | FJES_STAT("rx_dropped", stats64.rx_dropped), | |
49 | FJES_STAT("tx_dropped", stats64.tx_dropped), | |
50 | }; | |
51 | ||
21b7efbc TI |
52 | #define FJES_EP_STATS_LEN 14 |
53 | #define FJES_STATS_LEN \ | |
54 | (ARRAY_SIZE(fjes_gstrings_stats) + \ | |
55 | ((&((struct fjes_adapter *)netdev_priv(netdev))->hw)->max_epid - 1) * \ | |
56 | FJES_EP_STATS_LEN) | |
57 | ||
786eec27 TI |
58 | static void fjes_get_ethtool_stats(struct net_device *netdev, |
59 | struct ethtool_stats *stats, u64 *data) | |
60 | { | |
61 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
21b7efbc TI |
62 | struct fjes_hw *hw = &adapter->hw; |
63 | int epidx; | |
786eec27 TI |
64 | char *p; |
65 | int i; | |
66 | ||
67 | for (i = 0; i < ARRAY_SIZE(fjes_gstrings_stats); i++) { | |
68 | p = (char *)adapter + fjes_gstrings_stats[i].stat_offset; | |
69 | data[i] = (fjes_gstrings_stats[i].sizeof_stat == sizeof(u64)) | |
70 | ? *(u64 *)p : *(u32 *)p; | |
71 | } | |
21b7efbc TI |
72 | for (epidx = 0; epidx < hw->max_epid; epidx++) { |
73 | if (epidx == hw->my_epid) | |
74 | continue; | |
75 | data[i++] = hw->ep_shm_info[epidx].ep_stats | |
76 | .com_regist_buf_exec; | |
77 | data[i++] = hw->ep_shm_info[epidx].ep_stats | |
78 | .com_unregist_buf_exec; | |
79 | data[i++] = hw->ep_shm_info[epidx].ep_stats.send_intr_rx; | |
80 | data[i++] = hw->ep_shm_info[epidx].ep_stats.send_intr_unshare; | |
81 | data[i++] = hw->ep_shm_info[epidx].ep_stats | |
82 | .send_intr_zoneupdate; | |
83 | data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_rx; | |
84 | data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_unshare; | |
85 | data[i++] = hw->ep_shm_info[epidx].ep_stats.recv_intr_stop; | |
86 | data[i++] = hw->ep_shm_info[epidx].ep_stats | |
87 | .recv_intr_zoneupdate; | |
88 | data[i++] = hw->ep_shm_info[epidx].ep_stats.tx_buffer_full; | |
89 | data[i++] = hw->ep_shm_info[epidx].ep_stats | |
90 | .tx_dropped_not_shared; | |
91 | data[i++] = hw->ep_shm_info[epidx].ep_stats | |
92 | .tx_dropped_ver_mismatch; | |
93 | data[i++] = hw->ep_shm_info[epidx].ep_stats | |
94 | .tx_dropped_buf_size_mismatch; | |
95 | data[i++] = hw->ep_shm_info[epidx].ep_stats | |
96 | .tx_dropped_vlanid_mismatch; | |
97 | } | |
786eec27 TI |
98 | } |
99 | ||
100 | static void fjes_get_strings(struct net_device *netdev, | |
101 | u32 stringset, u8 *data) | |
102 | { | |
21b7efbc TI |
103 | struct fjes_adapter *adapter = netdev_priv(netdev); |
104 | struct fjes_hw *hw = &adapter->hw; | |
786eec27 TI |
105 | u8 *p = data; |
106 | int i; | |
107 | ||
108 | switch (stringset) { | |
109 | case ETH_SS_STATS: | |
110 | for (i = 0; i < ARRAY_SIZE(fjes_gstrings_stats); i++) { | |
111 | memcpy(p, fjes_gstrings_stats[i].stat_string, | |
112 | ETH_GSTRING_LEN); | |
113 | p += ETH_GSTRING_LEN; | |
114 | } | |
21b7efbc TI |
115 | for (i = 0; i < hw->max_epid; i++) { |
116 | if (i == hw->my_epid) | |
117 | continue; | |
118 | sprintf(p, "ep%u_com_regist_buf_exec", i); | |
119 | p += ETH_GSTRING_LEN; | |
120 | sprintf(p, "ep%u_com_unregist_buf_exec", i); | |
121 | p += ETH_GSTRING_LEN; | |
122 | sprintf(p, "ep%u_send_intr_rx", i); | |
123 | p += ETH_GSTRING_LEN; | |
124 | sprintf(p, "ep%u_send_intr_unshare", i); | |
125 | p += ETH_GSTRING_LEN; | |
126 | sprintf(p, "ep%u_send_intr_zoneupdate", i); | |
127 | p += ETH_GSTRING_LEN; | |
128 | sprintf(p, "ep%u_recv_intr_rx", i); | |
129 | p += ETH_GSTRING_LEN; | |
130 | sprintf(p, "ep%u_recv_intr_unshare", i); | |
131 | p += ETH_GSTRING_LEN; | |
132 | sprintf(p, "ep%u_recv_intr_stop", i); | |
133 | p += ETH_GSTRING_LEN; | |
134 | sprintf(p, "ep%u_recv_intr_zoneupdate", i); | |
135 | p += ETH_GSTRING_LEN; | |
136 | sprintf(p, "ep%u_tx_buffer_full", i); | |
137 | p += ETH_GSTRING_LEN; | |
138 | sprintf(p, "ep%u_tx_dropped_not_shared", i); | |
139 | p += ETH_GSTRING_LEN; | |
140 | sprintf(p, "ep%u_tx_dropped_ver_mismatch", i); | |
141 | p += ETH_GSTRING_LEN; | |
142 | sprintf(p, "ep%u_tx_dropped_buf_size_mismatch", i); | |
143 | p += ETH_GSTRING_LEN; | |
144 | sprintf(p, "ep%u_tx_dropped_vlanid_mismatch", i); | |
145 | p += ETH_GSTRING_LEN; | |
146 | } | |
786eec27 TI |
147 | break; |
148 | } | |
149 | } | |
150 | ||
151 | static int fjes_get_sset_count(struct net_device *netdev, int sset) | |
152 | { | |
153 | switch (sset) { | |
154 | case ETH_SS_STATS: | |
21b7efbc | 155 | return FJES_STATS_LEN; |
786eec27 TI |
156 | default: |
157 | return -EOPNOTSUPP; | |
158 | } | |
159 | } | |
160 | ||
161 | static void fjes_get_drvinfo(struct net_device *netdev, | |
162 | struct ethtool_drvinfo *drvinfo) | |
163 | { | |
164 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
165 | struct platform_device *plat_dev; | |
166 | ||
167 | plat_dev = adapter->plat_dev; | |
168 | ||
169 | strlcpy(drvinfo->driver, fjes_driver_name, sizeof(drvinfo->driver)); | |
170 | strlcpy(drvinfo->version, fjes_driver_version, | |
171 | sizeof(drvinfo->version)); | |
172 | ||
173 | strlcpy(drvinfo->fw_version, "none", sizeof(drvinfo->fw_version)); | |
174 | snprintf(drvinfo->bus_info, sizeof(drvinfo->bus_info), | |
175 | "platform:%s", plat_dev->name); | |
786eec27 TI |
176 | } |
177 | ||
031e5144 PR |
178 | static int fjes_get_link_ksettings(struct net_device *netdev, |
179 | struct ethtool_link_ksettings *ecmd) | |
786eec27 | 180 | { |
031e5144 PR |
181 | ethtool_link_ksettings_zero_link_mode(ecmd, supported); |
182 | ethtool_link_ksettings_zero_link_mode(ecmd, advertising); | |
183 | ecmd->base.duplex = DUPLEX_FULL; | |
184 | ecmd->base.autoneg = AUTONEG_DISABLE; | |
185 | ecmd->base.port = PORT_NONE; | |
186 | ecmd->base.speed = 20000; /* 20Gb/s */ | |
786eec27 TI |
187 | |
188 | return 0; | |
189 | } | |
190 | ||
462d8074 TI |
191 | static int fjes_get_regs_len(struct net_device *netdev) |
192 | { | |
193 | #define FJES_REGS_LEN 37 | |
194 | return FJES_REGS_LEN * sizeof(u32); | |
195 | } | |
196 | ||
197 | static void fjes_get_regs(struct net_device *netdev, | |
198 | struct ethtool_regs *regs, void *p) | |
199 | { | |
200 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
201 | struct fjes_hw *hw = &adapter->hw; | |
202 | u32 *regs_buff = p; | |
203 | ||
204 | memset(p, 0, FJES_REGS_LEN * sizeof(u32)); | |
205 | ||
206 | regs->version = 1; | |
207 | ||
208 | /* Information registers */ | |
209 | regs_buff[0] = rd32(XSCT_OWNER_EPID); | |
210 | regs_buff[1] = rd32(XSCT_MAX_EP); | |
211 | ||
212 | /* Device Control registers */ | |
213 | regs_buff[4] = rd32(XSCT_DCTL); | |
214 | ||
215 | /* Command Control registers */ | |
216 | regs_buff[8] = rd32(XSCT_CR); | |
217 | regs_buff[9] = rd32(XSCT_CS); | |
218 | regs_buff[10] = rd32(XSCT_SHSTSAL); | |
219 | regs_buff[11] = rd32(XSCT_SHSTSAH); | |
220 | ||
221 | regs_buff[13] = rd32(XSCT_REQBL); | |
222 | regs_buff[14] = rd32(XSCT_REQBAL); | |
223 | regs_buff[15] = rd32(XSCT_REQBAH); | |
224 | ||
225 | regs_buff[17] = rd32(XSCT_RESPBL); | |
226 | regs_buff[18] = rd32(XSCT_RESPBAL); | |
227 | regs_buff[19] = rd32(XSCT_RESPBAH); | |
228 | ||
229 | /* Interrupt Control registers */ | |
230 | regs_buff[32] = rd32(XSCT_IS); | |
231 | regs_buff[33] = rd32(XSCT_IMS); | |
232 | regs_buff[34] = rd32(XSCT_IMC); | |
233 | regs_buff[35] = rd32(XSCT_IG); | |
234 | regs_buff[36] = rd32(XSCT_ICTL); | |
235 | } | |
236 | ||
b6ba737d TI |
237 | static int fjes_set_dump(struct net_device *netdev, struct ethtool_dump *dump) |
238 | { | |
239 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
240 | struct fjes_hw *hw = &adapter->hw; | |
241 | int ret = 0; | |
242 | ||
243 | if (dump->flag) { | |
244 | if (hw->debug_mode) | |
245 | return -EPERM; | |
246 | ||
247 | hw->debug_mode = dump->flag; | |
248 | ||
249 | /* enable debug mode */ | |
250 | mutex_lock(&hw->hw_info.lock); | |
251 | ret = fjes_hw_start_debug(hw); | |
252 | mutex_unlock(&hw->hw_info.lock); | |
253 | ||
254 | if (ret) | |
255 | hw->debug_mode = 0; | |
256 | } else { | |
257 | if (!hw->debug_mode) | |
258 | return -EPERM; | |
259 | ||
260 | /* disable debug mode */ | |
261 | mutex_lock(&hw->hw_info.lock); | |
262 | ret = fjes_hw_stop_debug(hw); | |
263 | mutex_unlock(&hw->hw_info.lock); | |
264 | } | |
265 | ||
266 | return ret; | |
267 | } | |
268 | ||
269 | static int fjes_get_dump_flag(struct net_device *netdev, | |
270 | struct ethtool_dump *dump) | |
271 | { | |
272 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
273 | struct fjes_hw *hw = &adapter->hw; | |
274 | ||
275 | dump->len = hw->hw_info.trace_size; | |
276 | dump->version = 1; | |
277 | dump->flag = hw->debug_mode; | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | static int fjes_get_dump_data(struct net_device *netdev, | |
283 | struct ethtool_dump *dump, void *buf) | |
284 | { | |
285 | struct fjes_adapter *adapter = netdev_priv(netdev); | |
286 | struct fjes_hw *hw = &adapter->hw; | |
287 | int ret = 0; | |
288 | ||
289 | if (hw->hw_info.trace) | |
290 | memcpy(buf, hw->hw_info.trace, hw->hw_info.trace_size); | |
291 | else | |
292 | ret = -EPERM; | |
293 | ||
294 | return ret; | |
295 | } | |
296 | ||
786eec27 | 297 | static const struct ethtool_ops fjes_ethtool_ops = { |
786eec27 TI |
298 | .get_drvinfo = fjes_get_drvinfo, |
299 | .get_ethtool_stats = fjes_get_ethtool_stats, | |
300 | .get_strings = fjes_get_strings, | |
301 | .get_sset_count = fjes_get_sset_count, | |
462d8074 TI |
302 | .get_regs = fjes_get_regs, |
303 | .get_regs_len = fjes_get_regs_len, | |
b6ba737d TI |
304 | .set_dump = fjes_set_dump, |
305 | .get_dump_flag = fjes_get_dump_flag, | |
306 | .get_dump_data = fjes_get_dump_data, | |
031e5144 | 307 | .get_link_ksettings = fjes_get_link_ksettings, |
786eec27 TI |
308 | }; |
309 | ||
310 | void fjes_set_ethtool_ops(struct net_device *netdev) | |
311 | { | |
312 | netdev->ethtool_ops = &fjes_ethtool_ops; | |
313 | } |