diff options
author | Ron Mercer <ron.mercer@qlogic.com> | 2008-09-18 11:56:28 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-09-18 11:56:28 -0400 |
commit | c4e84bde1d595d857d3c74b49b9c45cc770df792 (patch) | |
tree | 28104fca89adea9ef12ada4f4b93337199695314 /drivers/net/qlge/qlge_ethtool.c | |
parent | 95252236e73e789dd186ce796a2abc60b3a61ebe (diff) |
qlge: New Qlogic 10Gb Ethernet Driver.
Signed-off-by: Ron Mercer <ron.mercer@qlogic.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/net/qlge/qlge_ethtool.c')
-rw-r--r-- | drivers/net/qlge/qlge_ethtool.c | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c new file mode 100644 index 000000000000..6457f8c4fdaa --- /dev/null +++ b/drivers/net/qlge/qlge_ethtool.c | |||
@@ -0,0 +1,415 @@ | |||
1 | #include <linux/kernel.h> | ||
2 | #include <linux/init.h> | ||
3 | #include <linux/types.h> | ||
4 | #include <linux/module.h> | ||
5 | #include <linux/list.h> | ||
6 | #include <linux/pci.h> | ||
7 | #include <linux/dma-mapping.h> | ||
8 | #include <linux/pagemap.h> | ||
9 | #include <linux/sched.h> | ||
10 | #include <linux/slab.h> | ||
11 | #include <linux/dmapool.h> | ||
12 | #include <linux/mempool.h> | ||
13 | #include <linux/spinlock.h> | ||
14 | #include <linux/kthread.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/ioport.h> | ||
18 | #include <linux/in.h> | ||
19 | #include <linux/ip.h> | ||
20 | #include <linux/ipv6.h> | ||
21 | #include <net/ipv6.h> | ||
22 | #include <linux/tcp.h> | ||
23 | #include <linux/udp.h> | ||
24 | #include <linux/if_arp.h> | ||
25 | #include <linux/if_ether.h> | ||
26 | #include <linux/netdevice.h> | ||
27 | #include <linux/etherdevice.h> | ||
28 | #include <linux/ethtool.h> | ||
29 | #include <linux/skbuff.h> | ||
30 | #include <linux/rtnetlink.h> | ||
31 | #include <linux/if_vlan.h> | ||
32 | #include <linux/init.h> | ||
33 | #include <linux/delay.h> | ||
34 | #include <linux/mm.h> | ||
35 | #include <linux/vmalloc.h> | ||
36 | |||
37 | #include <linux/version.h> | ||
38 | |||
39 | #include "qlge.h" | ||
40 | |||
41 | static int ql_update_ring_coalescing(struct ql_adapter *qdev) | ||
42 | { | ||
43 | int i, status = 0; | ||
44 | struct rx_ring *rx_ring; | ||
45 | struct cqicb *cqicb; | ||
46 | |||
47 | if (!netif_running(qdev->ndev)) | ||
48 | return status; | ||
49 | |||
50 | spin_lock(&qdev->hw_lock); | ||
51 | /* Skip the default queue, and update the outbound handler | ||
52 | * queues if they changed. | ||
53 | */ | ||
54 | cqicb = (struct cqicb *)&qdev->rx_ring[1]; | ||
55 | if (le16_to_cpu(cqicb->irq_delay) != qdev->tx_coalesce_usecs || | ||
56 | le16_to_cpu(cqicb->pkt_delay) != qdev->tx_max_coalesced_frames) { | ||
57 | for (i = 1; i < qdev->rss_ring_first_cq_id; i++, rx_ring++) { | ||
58 | rx_ring = &qdev->rx_ring[i]; | ||
59 | cqicb = (struct cqicb *)rx_ring; | ||
60 | cqicb->irq_delay = le16_to_cpu(qdev->tx_coalesce_usecs); | ||
61 | cqicb->pkt_delay = | ||
62 | le16_to_cpu(qdev->tx_max_coalesced_frames); | ||
63 | cqicb->flags = FLAGS_LI; | ||
64 | status = ql_write_cfg(qdev, cqicb, sizeof(cqicb), | ||
65 | CFG_LCQ, rx_ring->cq_id); | ||
66 | if (status) { | ||
67 | QPRINTK(qdev, IFUP, ERR, | ||
68 | "Failed to load CQICB.\n"); | ||
69 | goto exit; | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | /* Update the inbound (RSS) handler queues if they changed. */ | ||
75 | cqicb = (struct cqicb *)&qdev->rx_ring[qdev->rss_ring_first_cq_id]; | ||
76 | if (le16_to_cpu(cqicb->irq_delay) != qdev->rx_coalesce_usecs || | ||
77 | le16_to_cpu(cqicb->pkt_delay) != qdev->rx_max_coalesced_frames) { | ||
78 | for (i = qdev->rss_ring_first_cq_id; | ||
79 | i <= qdev->rss_ring_first_cq_id + qdev->rss_ring_count; | ||
80 | i++) { | ||
81 | rx_ring = &qdev->rx_ring[i]; | ||
82 | cqicb = (struct cqicb *)rx_ring; | ||
83 | cqicb->irq_delay = le16_to_cpu(qdev->rx_coalesce_usecs); | ||
84 | cqicb->pkt_delay = | ||
85 | le16_to_cpu(qdev->rx_max_coalesced_frames); | ||
86 | cqicb->flags = FLAGS_LI; | ||
87 | status = ql_write_cfg(qdev, cqicb, sizeof(cqicb), | ||
88 | CFG_LCQ, rx_ring->cq_id); | ||
89 | if (status) { | ||
90 | QPRINTK(qdev, IFUP, ERR, | ||
91 | "Failed to load CQICB.\n"); | ||
92 | goto exit; | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | exit: | ||
97 | spin_unlock(&qdev->hw_lock); | ||
98 | return status; | ||
99 | } | ||
100 | |||
101 | void ql_update_stats(struct ql_adapter *qdev) | ||
102 | { | ||
103 | u32 i; | ||
104 | u64 data; | ||
105 | u64 *iter = &qdev->nic_stats.tx_pkts; | ||
106 | |||
107 | spin_lock(&qdev->stats_lock); | ||
108 | if (ql_sem_spinlock(qdev, qdev->xg_sem_mask)) { | ||
109 | QPRINTK(qdev, DRV, ERR, | ||
110 | "Couldn't get xgmac sem.\n"); | ||
111 | goto quit; | ||
112 | } | ||
113 | /* | ||
114 | * Get TX statistics. | ||
115 | */ | ||
116 | for (i = 0x200; i < 0x280; i += 8) { | ||
117 | if (ql_read_xgmac_reg64(qdev, i, &data)) { | ||
118 | QPRINTK(qdev, DRV, ERR, | ||
119 | "Error reading status register 0x%.04x.\n", i); | ||
120 | goto end; | ||
121 | } else | ||
122 | *iter = data; | ||
123 | iter++; | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Get RX statistics. | ||
128 | */ | ||
129 | for (i = 0x300; i < 0x3d0; i += 8) { | ||
130 | if (ql_read_xgmac_reg64(qdev, i, &data)) { | ||
131 | QPRINTK(qdev, DRV, ERR, | ||
132 | "Error reading status register 0x%.04x.\n", i); | ||
133 | goto end; | ||
134 | } else | ||
135 | *iter = data; | ||
136 | iter++; | ||
137 | } | ||
138 | |||
139 | end: | ||
140 | ql_sem_unlock(qdev, qdev->xg_sem_mask); | ||
141 | quit: | ||
142 | spin_unlock(&qdev->stats_lock); | ||
143 | |||
144 | QL_DUMP_STAT(qdev); | ||
145 | |||
146 | return; | ||
147 | } | ||
148 | |||
149 | static char ql_stats_str_arr[][ETH_GSTRING_LEN] = { | ||
150 | {"tx_pkts"}, | ||
151 | {"tx_bytes"}, | ||
152 | {"tx_mcast_pkts"}, | ||
153 | {"tx_bcast_pkts"}, | ||
154 | {"tx_ucast_pkts"}, | ||
155 | {"tx_ctl_pkts"}, | ||
156 | {"tx_pause_pkts"}, | ||
157 | {"tx_64_pkts"}, | ||
158 | {"tx_65_to_127_pkts"}, | ||
159 | {"tx_128_to_255_pkts"}, | ||
160 | {"tx_256_511_pkts"}, | ||
161 | {"tx_512_to_1023_pkts"}, | ||
162 | {"tx_1024_to_1518_pkts"}, | ||
163 | {"tx_1519_to_max_pkts"}, | ||
164 | {"tx_undersize_pkts"}, | ||
165 | {"tx_oversize_pkts"}, | ||
166 | {"rx_bytes"}, | ||
167 | {"rx_bytes_ok"}, | ||
168 | {"rx_pkts"}, | ||
169 | {"rx_pkts_ok"}, | ||
170 | {"rx_bcast_pkts"}, | ||
171 | {"rx_mcast_pkts"}, | ||
172 | {"rx_ucast_pkts"}, | ||
173 | {"rx_undersize_pkts"}, | ||
174 | {"rx_oversize_pkts"}, | ||
175 | {"rx_jabber_pkts"}, | ||
176 | {"rx_undersize_fcerr_pkts"}, | ||
177 | {"rx_drop_events"}, | ||
178 | {"rx_fcerr_pkts"}, | ||
179 | {"rx_align_err"}, | ||
180 | {"rx_symbol_err"}, | ||
181 | {"rx_mac_err"}, | ||
182 | {"rx_ctl_pkts"}, | ||
183 | {"rx_pause_pkts"}, | ||
184 | {"rx_64_pkts"}, | ||
185 | {"rx_65_to_127_pkts"}, | ||
186 | {"rx_128_255_pkts"}, | ||
187 | {"rx_256_511_pkts"}, | ||
188 | {"rx_512_to_1023_pkts"}, | ||
189 | {"rx_1024_to_1518_pkts"}, | ||
190 | {"rx_1519_to_max_pkts"}, | ||
191 | {"rx_len_err_pkts"}, | ||
192 | }; | ||
193 | |||
194 | static void ql_get_strings(struct net_device *dev, u32 stringset, u8 *buf) | ||
195 | { | ||
196 | switch (stringset) { | ||
197 | case ETH_SS_STATS: | ||
198 | memcpy(buf, ql_stats_str_arr, sizeof(ql_stats_str_arr)); | ||
199 | break; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | static int ql_get_sset_count(struct net_device *dev, int sset) | ||
204 | { | ||
205 | switch (sset) { | ||
206 | case ETH_SS_STATS: | ||
207 | return ARRAY_SIZE(ql_stats_str_arr); | ||
208 | default: | ||
209 | return -EOPNOTSUPP; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | static void | ||
214 | ql_get_ethtool_stats(struct net_device *ndev, | ||
215 | struct ethtool_stats *stats, u64 *data) | ||
216 | { | ||
217 | struct ql_adapter *qdev = netdev_priv(ndev); | ||
218 | struct nic_stats *s = &qdev->nic_stats; | ||
219 | |||
220 | ql_update_stats(qdev); | ||
221 | |||
222 | *data++ = s->tx_pkts; | ||
223 | *data++ = s->tx_bytes; | ||
224 | *data++ = s->tx_mcast_pkts; | ||
225 | *data++ = s->tx_bcast_pkts; | ||
226 | *data++ = s->tx_ucast_pkts; | ||
227 | *data++ = s->tx_ctl_pkts; | ||
228 | *data++ = s->tx_pause_pkts; | ||
229 | *data++ = s->tx_64_pkt; | ||
230 | *data++ = s->tx_65_to_127_pkt; | ||
231 | *data++ = s->tx_128_to_255_pkt; | ||
232 | *data++ = s->tx_256_511_pkt; | ||
233 | *data++ = s->tx_512_to_1023_pkt; | ||
234 | *data++ = s->tx_1024_to_1518_pkt; | ||
235 | *data++ = s->tx_1519_to_max_pkt; | ||
236 | *data++ = s->tx_undersize_pkt; | ||
237 | *data++ = s->tx_oversize_pkt; | ||
238 | *data++ = s->rx_bytes; | ||
239 | *data++ = s->rx_bytes_ok; | ||
240 | *data++ = s->rx_pkts; | ||
241 | *data++ = s->rx_pkts_ok; | ||
242 | *data++ = s->rx_bcast_pkts; | ||
243 | *data++ = s->rx_mcast_pkts; | ||
244 | *data++ = s->rx_ucast_pkts; | ||
245 | *data++ = s->rx_undersize_pkts; | ||
246 | *data++ = s->rx_oversize_pkts; | ||
247 | *data++ = s->rx_jabber_pkts; | ||
248 | *data++ = s->rx_undersize_fcerr_pkts; | ||
249 | *data++ = s->rx_drop_events; | ||
250 | *data++ = s->rx_fcerr_pkts; | ||
251 | *data++ = s->rx_align_err; | ||
252 | *data++ = s->rx_symbol_err; | ||
253 | *data++ = s->rx_mac_err; | ||
254 | *data++ = s->rx_ctl_pkts; | ||
255 | *data++ = s->rx_pause_pkts; | ||
256 | *data++ = s->rx_64_pkts; | ||
257 | *data++ = s->rx_65_to_127_pkts; | ||
258 | *data++ = s->rx_128_255_pkts; | ||
259 | *data++ = s->rx_256_511_pkts; | ||
260 | *data++ = s->rx_512_to_1023_pkts; | ||
261 | *data++ = s->rx_1024_to_1518_pkts; | ||
262 | *data++ = s->rx_1519_to_max_pkts; | ||
263 | *data++ = s->rx_len_err_pkts; | ||
264 | } | ||
265 | |||
266 | static int ql_get_settings(struct net_device *ndev, | ||
267 | struct ethtool_cmd *ecmd) | ||
268 | { | ||
269 | struct ql_adapter *qdev = netdev_priv(ndev); | ||
270 | |||
271 | ecmd->supported = SUPPORTED_10000baseT_Full; | ||
272 | ecmd->advertising = ADVERTISED_10000baseT_Full; | ||
273 | ecmd->autoneg = AUTONEG_ENABLE; | ||
274 | ecmd->transceiver = XCVR_EXTERNAL; | ||
275 | if ((qdev->link_status & LINK_TYPE_MASK) == LINK_TYPE_10GBASET) { | ||
276 | ecmd->supported |= (SUPPORTED_TP | SUPPORTED_Autoneg); | ||
277 | ecmd->advertising |= (ADVERTISED_TP | ADVERTISED_Autoneg); | ||
278 | ecmd->port = PORT_TP; | ||
279 | } else { | ||
280 | ecmd->supported |= SUPPORTED_FIBRE; | ||
281 | ecmd->advertising |= ADVERTISED_FIBRE; | ||
282 | ecmd->port = PORT_FIBRE; | ||
283 | } | ||
284 | |||
285 | ecmd->speed = SPEED_10000; | ||
286 | ecmd->duplex = DUPLEX_FULL; | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static void ql_get_drvinfo(struct net_device *ndev, | ||
292 | struct ethtool_drvinfo *drvinfo) | ||
293 | { | ||
294 | struct ql_adapter *qdev = netdev_priv(ndev); | ||
295 | strncpy(drvinfo->driver, qlge_driver_name, 32); | ||
296 | strncpy(drvinfo->version, qlge_driver_version, 32); | ||
297 | strncpy(drvinfo->fw_version, "N/A", 32); | ||
298 | strncpy(drvinfo->bus_info, pci_name(qdev->pdev), 32); | ||
299 | drvinfo->n_stats = 0; | ||
300 | drvinfo->testinfo_len = 0; | ||
301 | drvinfo->regdump_len = 0; | ||
302 | drvinfo->eedump_len = 0; | ||
303 | } | ||
304 | |||
305 | static int ql_get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) | ||
306 | { | ||
307 | struct ql_adapter *qdev = netdev_priv(dev); | ||
308 | |||
309 | c->rx_coalesce_usecs = qdev->rx_coalesce_usecs; | ||
310 | c->tx_coalesce_usecs = qdev->tx_coalesce_usecs; | ||
311 | |||
312 | /* This chip coalesces as follows: | ||
313 | * If a packet arrives, hold off interrupts until | ||
314 | * cqicb->int_delay expires, but if no other packets arrive don't | ||
315 | * wait longer than cqicb->pkt_int_delay. But ethtool doesn't use a | ||
316 | * timer to coalesce on a frame basis. So, we have to take ethtool's | ||
317 | * max_coalesced_frames value and convert it to a delay in microseconds. | ||
318 | * We do this by using a basic thoughput of 1,000,000 frames per | ||
319 | * second @ (1024 bytes). This means one frame per usec. So it's a | ||
320 | * simple one to one ratio. | ||
321 | */ | ||
322 | c->rx_max_coalesced_frames = qdev->rx_max_coalesced_frames; | ||
323 | c->tx_max_coalesced_frames = qdev->tx_max_coalesced_frames; | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static int ql_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *c) | ||
329 | { | ||
330 | struct ql_adapter *qdev = netdev_priv(ndev); | ||
331 | |||
332 | /* Validate user parameters. */ | ||
333 | if (c->rx_coalesce_usecs > qdev->rx_ring_size / 2) | ||
334 | return -EINVAL; | ||
335 | /* Don't wait more than 10 usec. */ | ||
336 | if (c->rx_max_coalesced_frames > MAX_INTER_FRAME_WAIT) | ||
337 | return -EINVAL; | ||
338 | if (c->tx_coalesce_usecs > qdev->tx_ring_size / 2) | ||
339 | return -EINVAL; | ||
340 | if (c->tx_max_coalesced_frames > MAX_INTER_FRAME_WAIT) | ||
341 | return -EINVAL; | ||
342 | |||
343 | /* Verify a change took place before updating the hardware. */ | ||
344 | if (qdev->rx_coalesce_usecs == c->rx_coalesce_usecs && | ||
345 | qdev->tx_coalesce_usecs == c->tx_coalesce_usecs && | ||
346 | qdev->rx_max_coalesced_frames == c->rx_max_coalesced_frames && | ||
347 | qdev->tx_max_coalesced_frames == c->tx_max_coalesced_frames) | ||
348 | return 0; | ||
349 | |||
350 | qdev->rx_coalesce_usecs = c->rx_coalesce_usecs; | ||
351 | qdev->tx_coalesce_usecs = c->tx_coalesce_usecs; | ||
352 | qdev->rx_max_coalesced_frames = c->rx_max_coalesced_frames; | ||
353 | qdev->tx_max_coalesced_frames = c->tx_max_coalesced_frames; | ||
354 | |||
355 | return ql_update_ring_coalescing(qdev); | ||
356 | } | ||
357 | |||
358 | static u32 ql_get_rx_csum(struct net_device *netdev) | ||
359 | { | ||
360 | struct ql_adapter *qdev = netdev_priv(netdev); | ||
361 | return qdev->rx_csum; | ||
362 | } | ||
363 | |||
364 | static int ql_set_rx_csum(struct net_device *netdev, uint32_t data) | ||
365 | { | ||
366 | struct ql_adapter *qdev = netdev_priv(netdev); | ||
367 | qdev->rx_csum = data; | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static int ql_set_tso(struct net_device *ndev, uint32_t data) | ||
372 | { | ||
373 | |||
374 | if (data) { | ||
375 | ndev->features |= NETIF_F_TSO; | ||
376 | ndev->features |= NETIF_F_TSO6; | ||
377 | } else { | ||
378 | ndev->features &= ~NETIF_F_TSO; | ||
379 | ndev->features &= ~NETIF_F_TSO6; | ||
380 | } | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | static u32 ql_get_msglevel(struct net_device *ndev) | ||
385 | { | ||
386 | struct ql_adapter *qdev = netdev_priv(ndev); | ||
387 | return qdev->msg_enable; | ||
388 | } | ||
389 | |||
390 | static void ql_set_msglevel(struct net_device *ndev, u32 value) | ||
391 | { | ||
392 | struct ql_adapter *qdev = netdev_priv(ndev); | ||
393 | qdev->msg_enable = value; | ||
394 | } | ||
395 | |||
396 | const struct ethtool_ops qlge_ethtool_ops = { | ||
397 | .get_settings = ql_get_settings, | ||
398 | .get_drvinfo = ql_get_drvinfo, | ||
399 | .get_msglevel = ql_get_msglevel, | ||
400 | .set_msglevel = ql_set_msglevel, | ||
401 | .get_link = ethtool_op_get_link, | ||
402 | .get_rx_csum = ql_get_rx_csum, | ||
403 | .set_rx_csum = ql_set_rx_csum, | ||
404 | .get_tx_csum = ethtool_op_get_tx_csum, | ||
405 | .get_sg = ethtool_op_get_sg, | ||
406 | .set_sg = ethtool_op_set_sg, | ||
407 | .get_tso = ethtool_op_get_tso, | ||
408 | .set_tso = ql_set_tso, | ||
409 | .get_coalesce = ql_get_coalesce, | ||
410 | .set_coalesce = ql_set_coalesce, | ||
411 | .get_sset_count = ql_get_sset_count, | ||
412 | .get_strings = ql_get_strings, | ||
413 | .get_ethtool_stats = ql_get_ethtool_stats, | ||
414 | }; | ||
415 | |||