forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtyphoon.c
2672 lines (2276 loc) · 74.3 KB
/
typhoon.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* typhoon.c: A Linux Ethernet device driver for 3Com 3CR990 family of NICs */
/*
Written 2002-2004 by David Dillow <[email protected]>
Based on code written 1998-2000 by Donald Becker <[email protected]> and
Linux 2.2.x driver by David P. McLean <[email protected]>.
This software may be used and distributed according to the terms of
the GNU General Public License (GPL), incorporated herein by reference.
Drivers based on or derived from this code fall under the GPL and must
retain the authorship, copyright and license notice. This file is not
a complete program and may only be used when the entire operating
system is licensed under the GPL.
This software is available on a public web site. It may enable
cryptographic capabilities of the 3Com hardware, and may be
exported from the United States under License Exception "TSU"
pursuant to 15 C.F.R. Section 740.13(e).
This work was funded by the National Library of Medicine under
the Department of Energy project number 0274DD06D1 and NLM project
number Y1-LM-2015-01.
This driver is designed for the 3Com 3CR990 Family of cards with the
3XP Processor. It has been tested on x86 and sparc64.
KNOWN ISSUES:
*) The current firmware always strips the VLAN tag off, even if
we tell it not to. You should filter VLANs at the switch
as a workaround (good practice in any event) until we can
get this fixed.
*) Cannot DMA Rx packets to a 2 byte aligned address. Also firmware
issue. Hopefully 3Com will fix it.
*) Waiting for a command response takes 8ms due to non-preemptable
polling. Only significant for getting stats and creating
SAs, but an ugly wart never the less.
TODO:
*) Doesn't do IPSEC offloading. Yet. Keep yer pants on, it's coming.
*) Add more support for ethtool (especially for NIC stats)
*) Allow disabling of RX checksum offloading
*) Fix MAC changing to work while the interface is up
(Need to put commands on the TX ring, which changes
the locking)
*) Add in FCS to {rx,tx}_bytes, since the hardware doesn't. See
http://oss.sgi.com/cgi-bin/mesg.cgi?a=netdev&i=20031215152211.7003fe8e.rddunlap%40osdl.org
*/
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
* Setting to > 1518 effectively disables this feature.
*/
static int rx_copybreak = 200;
/* Should we use MMIO or Port IO?
* 0: Port IO
* 1: MMIO
* 2: Try MMIO, fallback to Port IO
*/
static unsigned int use_mmio = 2;
/* end user-configurable values */
/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
*/
static const int multicast_filter_limit = 32;
/* Operational parameters that are set at compile time. */
/* Keep the ring sizes a power of two for compile efficiency.
* The compiler will convert <unsigned>'%'<2^N> into a bit mask.
* Making the Tx ring too large decreases the effectiveness of channel
* bonding and packet priority.
* There are no ill effects from too-large receive rings.
*
* We don't currently use the Hi Tx ring so, don't make it very big.
*
* Beware that if we start using the Hi Tx ring, we will need to change
* typhoon_num_free_tx() and typhoon_tx_complete() to account for that.
*/
#define TXHI_ENTRIES 2
#define TXLO_ENTRIES 128
#define RX_ENTRIES 32
#define COMMAND_ENTRIES 16
#define RESPONSE_ENTRIES 32
#define COMMAND_RING_SIZE (COMMAND_ENTRIES * sizeof(struct cmd_desc))
#define RESPONSE_RING_SIZE (RESPONSE_ENTRIES * sizeof(struct resp_desc))
/* The 3XP will preload and remove 64 entries from the free buffer
* list, and we need one entry to keep the ring from wrapping, so
* to keep this a power of two, we use 128 entries.
*/
#define RXFREE_ENTRIES 128
#define RXENT_ENTRIES (RXFREE_ENTRIES - 1)
/* Operational parameters that usually are not changed. */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (2*HZ)
#define PKT_BUF_SZ 1536
#define DRV_MODULE_NAME "typhoon"
#define DRV_MODULE_VERSION "1.5.7"
#define DRV_MODULE_RELDATE "05/01/07"
#define PFX DRV_MODULE_NAME ": "
#define ERR_PFX KERN_ERR PFX
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/if_vlan.h>
#include <linux/crc32.h>
#include <linux/bitops.h>
#include <asm/processor.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/in6.h>
#include <asm/checksum.h>
#include <linux/version.h>
#include <linux/dma-mapping.h>
#include "typhoon.h"
#include "typhoon-firmware.h"
static char version[] __devinitdata =
"typhoon.c: version " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("David Dillow <[email protected]>");
MODULE_VERSION(DRV_MODULE_VERSION);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("3Com Typhoon Family (3C990, 3CR990, and variants)");
MODULE_PARM_DESC(rx_copybreak, "Packets smaller than this are copied and "
"the buffer given back to the NIC. Default "
"is 200.");
MODULE_PARM_DESC(use_mmio, "Use MMIO (1) or PIO(0) to access the NIC. "
"Default is to try MMIO and fallback to PIO.");
module_param(rx_copybreak, int, 0);
module_param(use_mmio, int, 0);
#if defined(NETIF_F_TSO) && MAX_SKB_FRAGS > 32
#warning Typhoon only supports 32 entries in its SG list for TSO, disabling TSO
#undef NETIF_F_TSO
#endif
#if TXLO_ENTRIES <= (2 * MAX_SKB_FRAGS)
#error TX ring too small!
#endif
struct typhoon_card_info {
char *name;
int capabilities;
};
#define TYPHOON_CRYPTO_NONE 0x00
#define TYPHOON_CRYPTO_DES 0x01
#define TYPHOON_CRYPTO_3DES 0x02
#define TYPHOON_CRYPTO_VARIABLE 0x04
#define TYPHOON_FIBER 0x08
#define TYPHOON_WAKEUP_NEEDS_RESET 0x10
enum typhoon_cards {
TYPHOON_TX = 0, TYPHOON_TX95, TYPHOON_TX97, TYPHOON_SVR,
TYPHOON_SVR95, TYPHOON_SVR97, TYPHOON_TXM, TYPHOON_BSVR,
TYPHOON_FX95, TYPHOON_FX97, TYPHOON_FX95SVR, TYPHOON_FX97SVR,
TYPHOON_FXM,
};
/* directly indexed by enum typhoon_cards, above */
static struct typhoon_card_info typhoon_card_info[] __devinitdata = {
{ "3Com Typhoon (3C990-TX)",
TYPHOON_CRYPTO_NONE},
{ "3Com Typhoon (3CR990-TX-95)",
TYPHOON_CRYPTO_DES},
{ "3Com Typhoon (3CR990-TX-97)",
TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES},
{ "3Com Typhoon (3C990SVR)",
TYPHOON_CRYPTO_NONE},
{ "3Com Typhoon (3CR990SVR95)",
TYPHOON_CRYPTO_DES},
{ "3Com Typhoon (3CR990SVR97)",
TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES},
{ "3Com Typhoon2 (3C990B-TX-M)",
TYPHOON_CRYPTO_VARIABLE},
{ "3Com Typhoon2 (3C990BSVR)",
TYPHOON_CRYPTO_VARIABLE},
{ "3Com Typhoon (3CR990-FX-95)",
TYPHOON_CRYPTO_DES | TYPHOON_FIBER},
{ "3Com Typhoon (3CR990-FX-97)",
TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES | TYPHOON_FIBER},
{ "3Com Typhoon (3CR990-FX-95 Server)",
TYPHOON_CRYPTO_DES | TYPHOON_FIBER},
{ "3Com Typhoon (3CR990-FX-97 Server)",
TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES | TYPHOON_FIBER},
{ "3Com Typhoon2 (3C990B-FX-97)",
TYPHOON_CRYPTO_VARIABLE | TYPHOON_FIBER},
};
/* Notes on the new subsystem numbering scheme:
* bits 0-1 indicate crypto capabilites: (0) variable, (1) DES, or (2) 3DES
* bit 4 indicates if this card has secured firmware (we don't support it)
* bit 8 indicates if this is a (0) copper or (1) fiber card
* bits 12-16 indicate card type: (0) client and (1) server
*/
static struct pci_device_id typhoon_pci_tbl[] = {
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,TYPHOON_TX },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_95,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX95 },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_97,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX97 },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
PCI_ANY_ID, 0x1000, 0, 0, TYPHOON_TXM },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FXM },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
PCI_ANY_ID, 0x2000, 0, 0, TYPHOON_BSVR },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
PCI_ANY_ID, 0x1101, 0, 0, TYPHOON_FX95 },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FX97 },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
PCI_ANY_ID, 0x2101, 0, 0, TYPHOON_FX95SVR },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
PCI_ANY_ID, 0x2102, 0, 0, TYPHOON_FX97SVR },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR95,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR95 },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR97,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR97 },
{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, typhoon_pci_tbl);
/* Define the shared memory area
* Align everything the 3XP will normally be using.
* We'll need to move/align txHi if we start using that ring.
*/
#define __3xp_aligned ____cacheline_aligned
struct typhoon_shared {
struct typhoon_interface iface;
struct typhoon_indexes indexes __3xp_aligned;
struct tx_desc txLo[TXLO_ENTRIES] __3xp_aligned;
struct rx_desc rxLo[RX_ENTRIES] __3xp_aligned;
struct rx_desc rxHi[RX_ENTRIES] __3xp_aligned;
struct cmd_desc cmd[COMMAND_ENTRIES] __3xp_aligned;
struct resp_desc resp[RESPONSE_ENTRIES] __3xp_aligned;
struct rx_free rxBuff[RXFREE_ENTRIES] __3xp_aligned;
u32 zeroWord;
struct tx_desc txHi[TXHI_ENTRIES];
} __attribute__ ((packed));
struct rxbuff_ent {
struct sk_buff *skb;
dma_addr_t dma_addr;
};
struct typhoon {
/* Tx cache line section */
struct transmit_ring txLoRing ____cacheline_aligned;
struct pci_dev * tx_pdev;
void __iomem *tx_ioaddr;
u32 txlo_dma_addr;
/* Irq/Rx cache line section */
void __iomem *ioaddr ____cacheline_aligned;
struct typhoon_indexes *indexes;
u8 awaiting_resp;
u8 duplex;
u8 speed;
u8 card_state;
struct basic_ring rxLoRing;
struct pci_dev * pdev;
struct net_device * dev;
spinlock_t state_lock;
struct vlan_group * vlgrp;
struct basic_ring rxHiRing;
struct basic_ring rxBuffRing;
struct rxbuff_ent rxbuffers[RXENT_ENTRIES];
/* general section */
spinlock_t command_lock ____cacheline_aligned;
struct basic_ring cmdRing;
struct basic_ring respRing;
struct net_device_stats stats;
struct net_device_stats stats_saved;
const char * name;
struct typhoon_shared * shared;
dma_addr_t shared_dma;
u16 xcvr_select;
u16 wol_events;
u32 offload;
/* unused stuff (future use) */
int capabilities;
struct transmit_ring txHiRing;
};
enum completion_wait_values {
NoWait = 0, WaitNoSleep, WaitSleep,
};
/* These are the values for the typhoon.card_state variable.
* These determine where the statistics will come from in get_stats().
* The sleep image does not support the statistics we need.
*/
enum state_values {
Sleeping = 0, Running,
};
/* PCI writes are not guaranteed to be posted in order, but outstanding writes
* cannot pass a read, so this forces current writes to post.
*/
#define typhoon_post_pci_writes(x) \
do { if(likely(use_mmio)) ioread32(x+TYPHOON_REG_HEARTBEAT); } while(0)
/* We'll wait up to six seconds for a reset, and half a second normally.
*/
#define TYPHOON_UDELAY 50
#define TYPHOON_RESET_TIMEOUT_SLEEP (6 * HZ)
#define TYPHOON_RESET_TIMEOUT_NOSLEEP ((6 * 1000000) / TYPHOON_UDELAY)
#define TYPHOON_WAIT_TIMEOUT ((1000000 / 2) / TYPHOON_UDELAY)
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 28)
#define typhoon_synchronize_irq(x) synchronize_irq()
#else
#define typhoon_synchronize_irq(x) synchronize_irq(x)
#endif
#if defined(NETIF_F_TSO)
#define skb_tso_size(x) (skb_shinfo(x)->tso_size)
#define TSO_NUM_DESCRIPTORS 2
#define TSO_OFFLOAD_ON TYPHOON_OFFLOAD_TCP_SEGMENT
#else
#define NETIF_F_TSO 0
#define skb_tso_size(x) 0
#define TSO_NUM_DESCRIPTORS 0
#define TSO_OFFLOAD_ON 0
#endif
static inline void
typhoon_inc_index(u32 *index, const int count, const int num_entries)
{
/* Increment a ring index -- we can use this for all rings execept
* the Rx rings, as they use different size descriptors
* otherwise, everything is the same size as a cmd_desc
*/
*index += count * sizeof(struct cmd_desc);
*index %= num_entries * sizeof(struct cmd_desc);
}
static inline void
typhoon_inc_cmd_index(u32 *index, const int count)
{
typhoon_inc_index(index, count, COMMAND_ENTRIES);
}
static inline void
typhoon_inc_resp_index(u32 *index, const int count)
{
typhoon_inc_index(index, count, RESPONSE_ENTRIES);
}
static inline void
typhoon_inc_rxfree_index(u32 *index, const int count)
{
typhoon_inc_index(index, count, RXFREE_ENTRIES);
}
static inline void
typhoon_inc_tx_index(u32 *index, const int count)
{
/* if we start using the Hi Tx ring, this needs updateing */
typhoon_inc_index(index, count, TXLO_ENTRIES);
}
static inline void
typhoon_inc_rx_index(u32 *index, const int count)
{
/* sizeof(struct rx_desc) != sizeof(struct cmd_desc) */
*index += count * sizeof(struct rx_desc);
*index %= RX_ENTRIES * sizeof(struct rx_desc);
}
static int
typhoon_reset(void __iomem *ioaddr, int wait_type)
{
int i, err = 0;
int timeout;
if(wait_type == WaitNoSleep)
timeout = TYPHOON_RESET_TIMEOUT_NOSLEEP;
else
timeout = TYPHOON_RESET_TIMEOUT_SLEEP;
iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
iowrite32(TYPHOON_RESET_ALL, ioaddr + TYPHOON_REG_SOFT_RESET);
typhoon_post_pci_writes(ioaddr);
udelay(1);
iowrite32(TYPHOON_RESET_NONE, ioaddr + TYPHOON_REG_SOFT_RESET);
if(wait_type != NoWait) {
for(i = 0; i < timeout; i++) {
if(ioread32(ioaddr + TYPHOON_REG_STATUS) ==
TYPHOON_STATUS_WAITING_FOR_HOST)
goto out;
if(wait_type == WaitSleep)
schedule_timeout_uninterruptible(1);
else
udelay(TYPHOON_UDELAY);
}
err = -ETIMEDOUT;
}
out:
iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
/* The 3XP seems to need a little extra time to complete the load
* of the sleep image before we can reliably boot it. Failure to
* do this occasionally results in a hung adapter after boot in
* typhoon_init_one() while trying to read the MAC address or
* putting the card to sleep. 3Com's driver waits 5ms, but
* that seems to be overkill. However, if we can sleep, we might
* as well give it that much time. Otherwise, we'll give it 500us,
* which should be enough (I've see it work well at 100us, but still
* saw occasional problems.)
*/
if(wait_type == WaitSleep)
msleep(5);
else
udelay(500);
return err;
}
static int
typhoon_wait_status(void __iomem *ioaddr, u32 wait_value)
{
int i, err = 0;
for(i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
if(ioread32(ioaddr + TYPHOON_REG_STATUS) == wait_value)
goto out;
udelay(TYPHOON_UDELAY);
}
err = -ETIMEDOUT;
out:
return err;
}
static inline void
typhoon_media_status(struct net_device *dev, struct resp_desc *resp)
{
if(resp->parm1 & TYPHOON_MEDIA_STAT_NO_LINK)
netif_carrier_off(dev);
else
netif_carrier_on(dev);
}
static inline void
typhoon_hello(struct typhoon *tp)
{
struct basic_ring *ring = &tp->cmdRing;
struct cmd_desc *cmd;
/* We only get a hello request if we've not sent anything to the
* card in a long while. If the lock is held, then we're in the
* process of issuing a command, so we don't need to respond.
*/
if(spin_trylock(&tp->command_lock)) {
cmd = (struct cmd_desc *)(ring->ringBase + ring->lastWrite);
typhoon_inc_cmd_index(&ring->lastWrite, 1);
INIT_COMMAND_NO_RESPONSE(cmd, TYPHOON_CMD_HELLO_RESP);
smp_wmb();
iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
spin_unlock(&tp->command_lock);
}
}
static int
typhoon_process_response(struct typhoon *tp, int resp_size,
struct resp_desc *resp_save)
{
struct typhoon_indexes *indexes = tp->indexes;
struct resp_desc *resp;
u8 *base = tp->respRing.ringBase;
int count, len, wrap_len;
u32 cleared;
u32 ready;
cleared = le32_to_cpu(indexes->respCleared);
ready = le32_to_cpu(indexes->respReady);
while(cleared != ready) {
resp = (struct resp_desc *)(base + cleared);
count = resp->numDesc + 1;
if(resp_save && resp->seqNo) {
if(count > resp_size) {
resp_save->flags = TYPHOON_RESP_ERROR;
goto cleanup;
}
wrap_len = 0;
len = count * sizeof(*resp);
if(unlikely(cleared + len > RESPONSE_RING_SIZE)) {
wrap_len = cleared + len - RESPONSE_RING_SIZE;
len = RESPONSE_RING_SIZE - cleared;
}
memcpy(resp_save, resp, len);
if(unlikely(wrap_len)) {
resp_save += len / sizeof(*resp);
memcpy(resp_save, base, wrap_len);
}
resp_save = NULL;
} else if(resp->cmd == TYPHOON_CMD_READ_MEDIA_STATUS) {
typhoon_media_status(tp->dev, resp);
} else if(resp->cmd == TYPHOON_CMD_HELLO_RESP) {
typhoon_hello(tp);
} else {
printk(KERN_ERR "%s: dumping unexpected response "
"0x%04x:%d:0x%02x:0x%04x:%08x:%08x\n",
tp->name, le16_to_cpu(resp->cmd),
resp->numDesc, resp->flags,
le16_to_cpu(resp->parm1),
le32_to_cpu(resp->parm2),
le32_to_cpu(resp->parm3));
}
cleanup:
typhoon_inc_resp_index(&cleared, count);
}
indexes->respCleared = cpu_to_le32(cleared);
wmb();
return (resp_save == NULL);
}
static inline int
typhoon_num_free(int lastWrite, int lastRead, int ringSize)
{
/* this works for all descriptors but rx_desc, as they are a
* different size than the cmd_desc -- everyone else is the same
*/
lastWrite /= sizeof(struct cmd_desc);
lastRead /= sizeof(struct cmd_desc);
return (ringSize + lastRead - lastWrite - 1) % ringSize;
}
static inline int
typhoon_num_free_cmd(struct typhoon *tp)
{
int lastWrite = tp->cmdRing.lastWrite;
int cmdCleared = le32_to_cpu(tp->indexes->cmdCleared);
return typhoon_num_free(lastWrite, cmdCleared, COMMAND_ENTRIES);
}
static inline int
typhoon_num_free_resp(struct typhoon *tp)
{
int respReady = le32_to_cpu(tp->indexes->respReady);
int respCleared = le32_to_cpu(tp->indexes->respCleared);
return typhoon_num_free(respReady, respCleared, RESPONSE_ENTRIES);
}
static inline int
typhoon_num_free_tx(struct transmit_ring *ring)
{
/* if we start using the Hi Tx ring, this needs updating */
return typhoon_num_free(ring->lastWrite, ring->lastRead, TXLO_ENTRIES);
}
static int
typhoon_issue_command(struct typhoon *tp, int num_cmd, struct cmd_desc *cmd,
int num_resp, struct resp_desc *resp)
{
struct typhoon_indexes *indexes = tp->indexes;
struct basic_ring *ring = &tp->cmdRing;
struct resp_desc local_resp;
int i, err = 0;
int got_resp;
int freeCmd, freeResp;
int len, wrap_len;
spin_lock(&tp->command_lock);
freeCmd = typhoon_num_free_cmd(tp);
freeResp = typhoon_num_free_resp(tp);
if(freeCmd < num_cmd || freeResp < num_resp) {
printk("%s: no descs for cmd, had (needed) %d (%d) cmd, "
"%d (%d) resp\n", tp->name, freeCmd, num_cmd,
freeResp, num_resp);
err = -ENOMEM;
goto out;
}
if(cmd->flags & TYPHOON_CMD_RESPOND) {
/* If we're expecting a response, but the caller hasn't given
* us a place to put it, we'll provide one.
*/
tp->awaiting_resp = 1;
if(resp == NULL) {
resp = &local_resp;
num_resp = 1;
}
}
wrap_len = 0;
len = num_cmd * sizeof(*cmd);
if(unlikely(ring->lastWrite + len > COMMAND_RING_SIZE)) {
wrap_len = ring->lastWrite + len - COMMAND_RING_SIZE;
len = COMMAND_RING_SIZE - ring->lastWrite;
}
memcpy(ring->ringBase + ring->lastWrite, cmd, len);
if(unlikely(wrap_len)) {
struct cmd_desc *wrap_ptr = cmd;
wrap_ptr += len / sizeof(*cmd);
memcpy(ring->ringBase, wrap_ptr, wrap_len);
}
typhoon_inc_cmd_index(&ring->lastWrite, num_cmd);
/* "I feel a presence... another warrior is on the the mesa."
*/
wmb();
iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
typhoon_post_pci_writes(tp->ioaddr);
if((cmd->flags & TYPHOON_CMD_RESPOND) == 0)
goto out;
/* Ugh. We'll be here about 8ms, spinning our thumbs, unable to
* preempt or do anything other than take interrupts. So, don't
* wait for a response unless you have to.
*
* I've thought about trying to sleep here, but we're called
* from many contexts that don't allow that. Also, given the way
* 3Com has implemented irq coalescing, we would likely timeout --
* this has been observed in real life!
*
* The big killer is we have to wait to get stats from the card,
* though we could go to a periodic refresh of those if we don't
* mind them getting somewhat stale. The rest of the waiting
* commands occur during open/close/suspend/resume, so they aren't
* time critical. Creating SAs in the future will also have to
* wait here.
*/
got_resp = 0;
for(i = 0; i < TYPHOON_WAIT_TIMEOUT && !got_resp; i++) {
if(indexes->respCleared != indexes->respReady)
got_resp = typhoon_process_response(tp, num_resp,
resp);
udelay(TYPHOON_UDELAY);
}
if(!got_resp) {
err = -ETIMEDOUT;
goto out;
}
/* Collect the error response even if we don't care about the
* rest of the response
*/
if(resp->flags & TYPHOON_RESP_ERROR)
err = -EIO;
out:
if(tp->awaiting_resp) {
tp->awaiting_resp = 0;
smp_wmb();
/* Ugh. If a response was added to the ring between
* the call to typhoon_process_response() and the clearing
* of tp->awaiting_resp, we could have missed the interrupt
* and it could hang in the ring an indeterminate amount of
* time. So, check for it, and interrupt ourselves if this
* is the case.
*/
if(indexes->respCleared != indexes->respReady)
iowrite32(1, tp->ioaddr + TYPHOON_REG_SELF_INTERRUPT);
}
spin_unlock(&tp->command_lock);
return err;
}
static void
typhoon_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
{
struct typhoon *tp = netdev_priv(dev);
struct cmd_desc xp_cmd;
int err;
spin_lock_bh(&tp->state_lock);
if(!tp->vlgrp != !grp) {
/* We've either been turned on for the first time, or we've
* been turned off. Update the 3XP.
*/
if(grp)
tp->offload |= TYPHOON_OFFLOAD_VLAN;
else
tp->offload &= ~TYPHOON_OFFLOAD_VLAN;
/* If the interface is up, the runtime is running -- and we
* must be up for the vlan core to call us.
*
* Do the command outside of the spin lock, as it is slow.
*/
INIT_COMMAND_WITH_RESPONSE(&xp_cmd,
TYPHOON_CMD_SET_OFFLOAD_TASKS);
xp_cmd.parm2 = tp->offload;
xp_cmd.parm3 = tp->offload;
spin_unlock_bh(&tp->state_lock);
err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
if(err < 0)
printk("%s: vlan offload error %d\n", tp->name, -err);
spin_lock_bh(&tp->state_lock);
}
/* now make the change visible */
tp->vlgrp = grp;
spin_unlock_bh(&tp->state_lock);
}
static void
typhoon_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
struct typhoon *tp = netdev_priv(dev);
spin_lock_bh(&tp->state_lock);
if(tp->vlgrp)
tp->vlgrp->vlan_devices[vid] = NULL;
spin_unlock_bh(&tp->state_lock);
}
static inline void
typhoon_tso_fill(struct sk_buff *skb, struct transmit_ring *txRing,
u32 ring_dma)
{
struct tcpopt_desc *tcpd;
u32 tcpd_offset = ring_dma;
tcpd = (struct tcpopt_desc *) (txRing->ringBase + txRing->lastWrite);
tcpd_offset += txRing->lastWrite;
tcpd_offset += offsetof(struct tcpopt_desc, bytesTx);
typhoon_inc_tx_index(&txRing->lastWrite, 1);
tcpd->flags = TYPHOON_OPT_DESC | TYPHOON_OPT_TCP_SEG;
tcpd->numDesc = 1;
tcpd->mss_flags = cpu_to_le16(skb_tso_size(skb));
tcpd->mss_flags |= TYPHOON_TSO_FIRST | TYPHOON_TSO_LAST;
tcpd->respAddrLo = cpu_to_le32(tcpd_offset);
tcpd->bytesTx = cpu_to_le32(skb->len);
tcpd->status = 0;
}
static int
typhoon_start_tx(struct sk_buff *skb, struct net_device *dev)
{
struct typhoon *tp = netdev_priv(dev);
struct transmit_ring *txRing;
struct tx_desc *txd, *first_txd;
dma_addr_t skb_dma;
int numDesc;
/* we have two rings to choose from, but we only use txLo for now
* If we start using the Hi ring as well, we'll need to update
* typhoon_stop_runtime(), typhoon_interrupt(), typhoon_num_free_tx(),
* and TXHI_ENTIRES to match, as well as update the TSO code below
* to get the right DMA address
*/
txRing = &tp->txLoRing;
/* We need one descriptor for each fragment of the sk_buff, plus the
* one for the ->data area of it.
*
* The docs say a maximum of 16 fragment descriptors per TCP option
* descriptor, then make a new packet descriptor and option descriptor
* for the next 16 fragments. The engineers say just an option
* descriptor is needed. I've tested up to 26 fragments with a single
* packet descriptor/option descriptor combo, so I use that for now.
*
* If problems develop with TSO, check this first.
*/
numDesc = skb_shinfo(skb)->nr_frags + 1;
if(skb_tso_size(skb))
numDesc++;
/* When checking for free space in the ring, we need to also
* account for the initial Tx descriptor, and we always must leave
* at least one descriptor unused in the ring so that it doesn't
* wrap and look empty.
*
* The only time we should loop here is when we hit the race
* between marking the queue awake and updating the cleared index.
* Just loop and it will appear. This comes from the acenic driver.
*/
while(unlikely(typhoon_num_free_tx(txRing) < (numDesc + 2)))
smp_rmb();
first_txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);
typhoon_inc_tx_index(&txRing->lastWrite, 1);
first_txd->flags = TYPHOON_TX_DESC | TYPHOON_DESC_VALID;
first_txd->numDesc = 0;
first_txd->len = 0;
first_txd->addr = (u64)((unsigned long) skb) & 0xffffffff;
first_txd->addrHi = (u64)((unsigned long) skb) >> 32;
first_txd->processFlags = 0;
if(skb->ip_summed == CHECKSUM_HW) {
/* The 3XP will figure out if this is UDP/TCP */
first_txd->processFlags |= TYPHOON_TX_PF_TCP_CHKSUM;
first_txd->processFlags |= TYPHOON_TX_PF_UDP_CHKSUM;
first_txd->processFlags |= TYPHOON_TX_PF_IP_CHKSUM;
}
if(vlan_tx_tag_present(skb)) {
first_txd->processFlags |=
TYPHOON_TX_PF_INSERT_VLAN | TYPHOON_TX_PF_VLAN_PRIORITY;
first_txd->processFlags |=
cpu_to_le32(htons(vlan_tx_tag_get(skb)) <<
TYPHOON_TX_PF_VLAN_TAG_SHIFT);
}
if(skb_tso_size(skb)) {
first_txd->processFlags |= TYPHOON_TX_PF_TCP_SEGMENT;
first_txd->numDesc++;
typhoon_tso_fill(skb, txRing, tp->txlo_dma_addr);
}
txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);
typhoon_inc_tx_index(&txRing->lastWrite, 1);
/* No need to worry about padding packet -- the firmware pads
* it with zeros to ETH_ZLEN for us.
*/
if(skb_shinfo(skb)->nr_frags == 0) {
skb_dma = pci_map_single(tp->tx_pdev, skb->data, skb->len,
PCI_DMA_TODEVICE);
txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
txd->len = cpu_to_le16(skb->len);
txd->addr = cpu_to_le32(skb_dma);
txd->addrHi = 0;
first_txd->numDesc++;
} else {
int i, len;
len = skb_headlen(skb);
skb_dma = pci_map_single(tp->tx_pdev, skb->data, len,
PCI_DMA_TODEVICE);
txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
txd->len = cpu_to_le16(len);
txd->addr = cpu_to_le32(skb_dma);
txd->addrHi = 0;
first_txd->numDesc++;
for(i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
void *frag_addr;
txd = (struct tx_desc *) (txRing->ringBase +
txRing->lastWrite);
typhoon_inc_tx_index(&txRing->lastWrite, 1);
len = frag->size;
frag_addr = (void *) page_address(frag->page) +
frag->page_offset;
skb_dma = pci_map_single(tp->tx_pdev, frag_addr, len,
PCI_DMA_TODEVICE);
txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
txd->len = cpu_to_le16(len);
txd->addr = cpu_to_le32(skb_dma);
txd->addrHi = 0;
first_txd->numDesc++;
}
}
/* Kick the 3XP
*/
wmb();
iowrite32(txRing->lastWrite, tp->tx_ioaddr + txRing->writeRegister);
dev->trans_start = jiffies;
/* If we don't have room to put the worst case packet on the
* queue, then we must stop the queue. We need 2 extra
* descriptors -- one to prevent ring wrap, and one for the
* Tx header.
*/
numDesc = MAX_SKB_FRAGS + TSO_NUM_DESCRIPTORS + 1;
if(typhoon_num_free_tx(txRing) < (numDesc + 2)) {
netif_stop_queue(dev);
/* A Tx complete IRQ could have gotten inbetween, making
* the ring free again. Only need to recheck here, since
* Tx is serialized.
*/
if(typhoon_num_free_tx(txRing) >= (numDesc + 2))
netif_wake_queue(dev);
}
return 0;
}
static void
typhoon_set_rx_mode(struct net_device *dev)
{
struct typhoon *tp = netdev_priv(dev);
struct cmd_desc xp_cmd;
u32 mc_filter[2];
u16 filter;
filter = TYPHOON_RX_FILTER_DIRECTED | TYPHOON_RX_FILTER_BROADCAST;
if(dev->flags & IFF_PROMISC) {
printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n",
dev->name);
filter |= TYPHOON_RX_FILTER_PROMISCOUS;
} else if((dev->mc_count > multicast_filter_limit) ||
(dev->flags & IFF_ALLMULTI)) {
/* Too many to match, or accept all multicasts. */
filter |= TYPHOON_RX_FILTER_ALL_MCAST;
} else if(dev->mc_count) {
struct dev_mc_list *mclist;
int i;
memset(mc_filter, 0, sizeof(mc_filter));
for(i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
i++, mclist = mclist->next) {
int bit = ether_crc(ETH_ALEN, mclist->dmi_addr) & 0x3f;
mc_filter[bit >> 5] |= 1 << (bit & 0x1f);
}
INIT_COMMAND_NO_RESPONSE(&xp_cmd,
TYPHOON_CMD_SET_MULTICAST_HASH);
xp_cmd.parm1 = TYPHOON_MCAST_HASH_SET;
xp_cmd.parm2 = cpu_to_le32(mc_filter[0]);
xp_cmd.parm3 = cpu_to_le32(mc_filter[1]);
typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
filter |= TYPHOON_RX_FILTER_MCAST_HASH;
}
INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_RX_FILTER);
xp_cmd.parm1 = filter;
typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
}
static int
typhoon_do_get_stats(struct typhoon *tp)
{
struct net_device_stats *stats = &tp->stats;
struct net_device_stats *saved = &tp->stats_saved;
struct cmd_desc xp_cmd;
struct resp_desc xp_resp[7];
struct stats_resp *s = (struct stats_resp *) xp_resp;
int err;
INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_STATS);
err = typhoon_issue_command(tp, 1, &xp_cmd, 7, xp_resp);
if(err < 0)
return err;
/* 3Com's Linux driver uses txMultipleCollisions as it's
* collisions value, but there is some other collision info as well...
*
* The extra status reported would be a good candidate for
* ethtool_ops->get_{strings,stats}()
*/
stats->tx_packets = le32_to_cpu(s->txPackets);
stats->tx_bytes = le32_to_cpu(s->txBytes);
stats->tx_errors = le32_to_cpu(s->txCarrierLost);
stats->tx_carrier_errors = le32_to_cpu(s->txCarrierLost);
stats->collisions = le32_to_cpu(s->txMultipleCollisions);
stats->rx_packets = le32_to_cpu(s->rxPacketsGood);
stats->rx_bytes = le32_to_cpu(s->rxBytesGood);