forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsb1250-mac.c
2924 lines (2337 loc) · 70.3 KB
/
sb1250-mac.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
/*
* Copyright (C) 2001,2002,2003 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
* This driver is designed for the Broadcom SiByte SOC built-in
* Ethernet controllers. Written by Mitch Lichtenberg at Broadcom Corp.
*/
#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/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/config.h>
#include <linux/bitops.h>
#include <asm/processor.h> /* Processor type for cache alignment. */
#include <asm/io.h>
#include <asm/cache.h>
/* This is only here until the firmware is ready. In that case,
the firmware leaves the ethernet address in the register for us. */
#ifdef CONFIG_SIBYTE_STANDALONE
#define SBMAC_ETH0_HWADDR "40:00:00:00:01:00"
#define SBMAC_ETH1_HWADDR "40:00:00:00:01:01"
#define SBMAC_ETH2_HWADDR "40:00:00:00:01:02"
#endif
/* These identify the driver base version and may not be removed. */
#if 0
static char version1[] __devinitdata =
"sb1250-mac.c:1.00 1/11/2001 Written by Mitch Lichtenberg\n";
#endif
/* Operational parameters that usually are not changed. */
#define CONFIG_SBMAC_COALESCE
#define MAX_UNITS 3 /* More are supported, limit only on options */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT (2*HZ)
MODULE_AUTHOR("Mitch Lichtenberg (Broadcom Corp.)");
MODULE_DESCRIPTION("Broadcom SiByte SOC GB Ethernet driver");
/* A few user-configurable values which may be modified when a driver
module is loaded. */
/* 1 normal messages, 0 quiet .. 7 verbose. */
static int debug = 1;
module_param(debug, int, S_IRUGO);
MODULE_PARM_DESC(debug, "Debug messages");
/* mii status msgs */
static int noisy_mii = 1;
module_param(noisy_mii, int, S_IRUGO);
MODULE_PARM_DESC(noisy_mii, "MII status messages");
/* Used to pass the media type, etc.
Both 'options[]' and 'full_duplex[]' should exist for driver
interoperability.
The media type is usually passed in 'options[]'.
*/
#ifdef MODULE
static int options[MAX_UNITS] = {-1, -1, -1};
module_param_array(options, int, NULL, S_IRUGO);
MODULE_PARM_DESC(options, "1-" __MODULE_STRING(MAX_UNITS));
static int full_duplex[MAX_UNITS] = {-1, -1, -1};
module_param_array(full_duplex, int, NULL, S_IRUGO);
MODULE_PARM_DESC(full_duplex, "1-" __MODULE_STRING(MAX_UNITS));
#endif
#ifdef CONFIG_SBMAC_COALESCE
static int int_pktcnt = 0;
module_param(int_pktcnt, int, S_IRUGO);
MODULE_PARM_DESC(int_pktcnt, "Packet count");
static int int_timeout = 0;
module_param(int_timeout, int, S_IRUGO);
MODULE_PARM_DESC(int_timeout, "Timeout value");
#endif
#include <asm/sibyte/sb1250.h>
#include <asm/sibyte/sb1250_defs.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_mac.h>
#include <asm/sibyte/sb1250_dma.h>
#include <asm/sibyte/sb1250_int.h>
#include <asm/sibyte/sb1250_scd.h>
/**********************************************************************
* Simple types
********************************************************************* */
typedef enum { sbmac_speed_auto, sbmac_speed_10,
sbmac_speed_100, sbmac_speed_1000 } sbmac_speed_t;
typedef enum { sbmac_duplex_auto, sbmac_duplex_half,
sbmac_duplex_full } sbmac_duplex_t;
typedef enum { sbmac_fc_auto, sbmac_fc_disabled, sbmac_fc_frame,
sbmac_fc_collision, sbmac_fc_carrier } sbmac_fc_t;
typedef enum { sbmac_state_uninit, sbmac_state_off, sbmac_state_on,
sbmac_state_broken } sbmac_state_t;
/**********************************************************************
* Macros
********************************************************************* */
#define SBDMA_NEXTBUF(d,f) ((((d)->f+1) == (d)->sbdma_dscrtable_end) ? \
(d)->sbdma_dscrtable : (d)->f+1)
#define NUMCACHEBLKS(x) (((x)+SMP_CACHE_BYTES-1)/SMP_CACHE_BYTES)
#define SBMAC_MAX_TXDESCR 32
#define SBMAC_MAX_RXDESCR 32
#define ETHER_ALIGN 2
#define ETHER_ADDR_LEN 6
#define ENET_PACKET_SIZE 1518
/*#define ENET_PACKET_SIZE 9216 */
/**********************************************************************
* DMA Descriptor structure
********************************************************************* */
typedef struct sbdmadscr_s {
uint64_t dscr_a;
uint64_t dscr_b;
} sbdmadscr_t;
typedef unsigned long paddr_t;
/**********************************************************************
* DMA Controller structure
********************************************************************* */
typedef struct sbmacdma_s {
/*
* This stuff is used to identify the channel and the registers
* associated with it.
*/
struct sbmac_softc *sbdma_eth; /* back pointer to associated MAC */
int sbdma_channel; /* channel number */
int sbdma_txdir; /* direction (1=transmit) */
int sbdma_maxdescr; /* total # of descriptors in ring */
#ifdef CONFIG_SBMAC_COALESCE
int sbdma_int_pktcnt; /* # descriptors rx/tx before interrupt*/
int sbdma_int_timeout; /* # usec rx/tx interrupt */
#endif
volatile void __iomem *sbdma_config0; /* DMA config register 0 */
volatile void __iomem *sbdma_config1; /* DMA config register 1 */
volatile void __iomem *sbdma_dscrbase; /* Descriptor base address */
volatile void __iomem *sbdma_dscrcnt; /* Descriptor count register */
volatile void __iomem *sbdma_curdscr; /* current descriptor address */
/*
* This stuff is for maintenance of the ring
*/
sbdmadscr_t *sbdma_dscrtable; /* base of descriptor table */
sbdmadscr_t *sbdma_dscrtable_end; /* end of descriptor table */
struct sk_buff **sbdma_ctxtable; /* context table, one per descr */
paddr_t sbdma_dscrtable_phys; /* and also the phys addr */
sbdmadscr_t *sbdma_addptr; /* next dscr for sw to add */
sbdmadscr_t *sbdma_remptr; /* next dscr for sw to remove */
} sbmacdma_t;
/**********************************************************************
* Ethernet softc structure
********************************************************************* */
struct sbmac_softc {
/*
* Linux-specific things
*/
struct net_device *sbm_dev; /* pointer to linux device */
spinlock_t sbm_lock; /* spin lock */
struct timer_list sbm_timer; /* for monitoring MII */
struct net_device_stats sbm_stats;
int sbm_devflags; /* current device flags */
int sbm_phy_oldbmsr;
int sbm_phy_oldanlpar;
int sbm_phy_oldk1stsr;
int sbm_phy_oldlinkstat;
int sbm_buffersize;
unsigned char sbm_phys[2];
/*
* Controller-specific things
*/
volatile void __iomem *sbm_base; /* MAC's base address */
sbmac_state_t sbm_state; /* current state */
volatile void __iomem *sbm_macenable; /* MAC Enable Register */
volatile void __iomem *sbm_maccfg; /* MAC Configuration Register */
volatile void __iomem *sbm_fifocfg; /* FIFO configuration register */
volatile void __iomem *sbm_framecfg; /* Frame configuration register */
volatile void __iomem *sbm_rxfilter; /* receive filter register */
volatile void __iomem *sbm_isr; /* Interrupt status register */
volatile void __iomem *sbm_imr; /* Interrupt mask register */
volatile void __iomem *sbm_mdio; /* MDIO register */
sbmac_speed_t sbm_speed; /* current speed */
sbmac_duplex_t sbm_duplex; /* current duplex */
sbmac_fc_t sbm_fc; /* current flow control setting */
unsigned char sbm_hwaddr[ETHER_ADDR_LEN];
sbmacdma_t sbm_txdma; /* for now, only use channel 0 */
sbmacdma_t sbm_rxdma;
int rx_hw_checksum;
int sbe_idx;
};
/**********************************************************************
* Externs
********************************************************************* */
/**********************************************************************
* Prototypes
********************************************************************* */
static void sbdma_initctx(sbmacdma_t *d,
struct sbmac_softc *s,
int chan,
int txrx,
int maxdescr);
static void sbdma_channel_start(sbmacdma_t *d, int rxtx);
static int sbdma_add_rcvbuffer(sbmacdma_t *d,struct sk_buff *m);
static int sbdma_add_txbuffer(sbmacdma_t *d,struct sk_buff *m);
static void sbdma_emptyring(sbmacdma_t *d);
static void sbdma_fillring(sbmacdma_t *d);
static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d);
static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d);
static int sbmac_initctx(struct sbmac_softc *s);
static void sbmac_channel_start(struct sbmac_softc *s);
static void sbmac_channel_stop(struct sbmac_softc *s);
static sbmac_state_t sbmac_set_channel_state(struct sbmac_softc *,sbmac_state_t);
static void sbmac_promiscuous_mode(struct sbmac_softc *sc,int onoff);
static uint64_t sbmac_addr2reg(unsigned char *ptr);
static irqreturn_t sbmac_intr(int irq,void *dev_instance,struct pt_regs *rgs);
static int sbmac_start_tx(struct sk_buff *skb, struct net_device *dev);
static void sbmac_setmulti(struct sbmac_softc *sc);
static int sbmac_init(struct net_device *dev, int idx);
static int sbmac_set_speed(struct sbmac_softc *s,sbmac_speed_t speed);
static int sbmac_set_duplex(struct sbmac_softc *s,sbmac_duplex_t duplex,sbmac_fc_t fc);
static int sbmac_open(struct net_device *dev);
static void sbmac_timer(unsigned long data);
static void sbmac_tx_timeout (struct net_device *dev);
static struct net_device_stats *sbmac_get_stats(struct net_device *dev);
static void sbmac_set_rx_mode(struct net_device *dev);
static int sbmac_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static int sbmac_close(struct net_device *dev);
static int sbmac_mii_poll(struct sbmac_softc *s,int noisy);
static int sbmac_mii_probe(struct net_device *dev);
static void sbmac_mii_sync(struct sbmac_softc *s);
static void sbmac_mii_senddata(struct sbmac_softc *s,unsigned int data, int bitcnt);
static unsigned int sbmac_mii_read(struct sbmac_softc *s,int phyaddr,int regidx);
static void sbmac_mii_write(struct sbmac_softc *s,int phyaddr,int regidx,
unsigned int regval);
/**********************************************************************
* Globals
********************************************************************* */
static uint64_t sbmac_orig_hwaddr[MAX_UNITS];
/**********************************************************************
* MDIO constants
********************************************************************* */
#define MII_COMMAND_START 0x01
#define MII_COMMAND_READ 0x02
#define MII_COMMAND_WRITE 0x01
#define MII_COMMAND_ACK 0x02
#define BMCR_RESET 0x8000
#define BMCR_LOOPBACK 0x4000
#define BMCR_SPEED0 0x2000
#define BMCR_ANENABLE 0x1000
#define BMCR_POWERDOWN 0x0800
#define BMCR_ISOLATE 0x0400
#define BMCR_RESTARTAN 0x0200
#define BMCR_DUPLEX 0x0100
#define BMCR_COLTEST 0x0080
#define BMCR_SPEED1 0x0040
#define BMCR_SPEED1000 BMCR_SPEED1
#define BMCR_SPEED100 BMCR_SPEED0
#define BMCR_SPEED10 0
#define BMSR_100BT4 0x8000
#define BMSR_100BT_FDX 0x4000
#define BMSR_100BT_HDX 0x2000
#define BMSR_10BT_FDX 0x1000
#define BMSR_10BT_HDX 0x0800
#define BMSR_100BT2_FDX 0x0400
#define BMSR_100BT2_HDX 0x0200
#define BMSR_1000BT_XSR 0x0100
#define BMSR_PRESUP 0x0040
#define BMSR_ANCOMPLT 0x0020
#define BMSR_REMFAULT 0x0010
#define BMSR_AUTONEG 0x0008
#define BMSR_LINKSTAT 0x0004
#define BMSR_JABDETECT 0x0002
#define BMSR_EXTCAPAB 0x0001
#define PHYIDR1 0x2000
#define PHYIDR2 0x5C60
#define ANAR_NP 0x8000
#define ANAR_RF 0x2000
#define ANAR_ASYPAUSE 0x0800
#define ANAR_PAUSE 0x0400
#define ANAR_T4 0x0200
#define ANAR_TXFD 0x0100
#define ANAR_TXHD 0x0080
#define ANAR_10FD 0x0040
#define ANAR_10HD 0x0020
#define ANAR_PSB 0x0001
#define ANLPAR_NP 0x8000
#define ANLPAR_ACK 0x4000
#define ANLPAR_RF 0x2000
#define ANLPAR_ASYPAUSE 0x0800
#define ANLPAR_PAUSE 0x0400
#define ANLPAR_T4 0x0200
#define ANLPAR_TXFD 0x0100
#define ANLPAR_TXHD 0x0080
#define ANLPAR_10FD 0x0040
#define ANLPAR_10HD 0x0020
#define ANLPAR_PSB 0x0001 /* 802.3 */
#define ANER_PDF 0x0010
#define ANER_LPNPABLE 0x0008
#define ANER_NPABLE 0x0004
#define ANER_PAGERX 0x0002
#define ANER_LPANABLE 0x0001
#define ANNPTR_NP 0x8000
#define ANNPTR_MP 0x2000
#define ANNPTR_ACK2 0x1000
#define ANNPTR_TOGTX 0x0800
#define ANNPTR_CODE 0x0008
#define ANNPRR_NP 0x8000
#define ANNPRR_MP 0x2000
#define ANNPRR_ACK3 0x1000
#define ANNPRR_TOGTX 0x0800
#define ANNPRR_CODE 0x0008
#define K1TCR_TESTMODE 0x0000
#define K1TCR_MSMCE 0x1000
#define K1TCR_MSCV 0x0800
#define K1TCR_RPTR 0x0400
#define K1TCR_1000BT_FDX 0x200
#define K1TCR_1000BT_HDX 0x100
#define K1STSR_MSMCFLT 0x8000
#define K1STSR_MSCFGRES 0x4000
#define K1STSR_LRSTAT 0x2000
#define K1STSR_RRSTAT 0x1000
#define K1STSR_LP1KFD 0x0800
#define K1STSR_LP1KHD 0x0400
#define K1STSR_LPASMDIR 0x0200
#define K1SCR_1KX_FDX 0x8000
#define K1SCR_1KX_HDX 0x4000
#define K1SCR_1KT_FDX 0x2000
#define K1SCR_1KT_HDX 0x1000
#define STRAP_PHY1 0x0800
#define STRAP_NCMODE 0x0400
#define STRAP_MANMSCFG 0x0200
#define STRAP_ANENABLE 0x0100
#define STRAP_MSVAL 0x0080
#define STRAP_1KHDXADV 0x0010
#define STRAP_1KFDXADV 0x0008
#define STRAP_100ADV 0x0004
#define STRAP_SPEEDSEL 0x0000
#define STRAP_SPEED100 0x0001
#define PHYSUP_SPEED1000 0x10
#define PHYSUP_SPEED100 0x08
#define PHYSUP_SPEED10 0x00
#define PHYSUP_LINKUP 0x04
#define PHYSUP_FDX 0x02
#define MII_BMCR 0x00 /* Basic mode control register (rw) */
#define MII_BMSR 0x01 /* Basic mode status register (ro) */
#define MII_PHYIDR1 0x02
#define MII_PHYIDR2 0x03
#define MII_K1STSR 0x0A /* 1K Status Register (ro) */
#define MII_ANLPAR 0x05 /* Autonegotiation lnk partner abilities (rw) */
#define M_MAC_MDIO_DIR_OUTPUT 0 /* for clarity */
#define ENABLE 1
#define DISABLE 0
/**********************************************************************
* SBMAC_MII_SYNC(s)
*
* Synchronize with the MII - send a pattern of bits to the MII
* that will guarantee that it is ready to accept a command.
*
* Input parameters:
* s - sbmac structure
*
* Return value:
* nothing
********************************************************************* */
static void sbmac_mii_sync(struct sbmac_softc *s)
{
int cnt;
uint64_t bits;
int mac_mdio_genc;
mac_mdio_genc = __raw_readq(s->sbm_mdio) & M_MAC_GENC;
bits = M_MAC_MDIO_DIR_OUTPUT | M_MAC_MDIO_OUT;
__raw_writeq(bits | mac_mdio_genc, s->sbm_mdio);
for (cnt = 0; cnt < 32; cnt++) {
__raw_writeq(bits | M_MAC_MDC | mac_mdio_genc, s->sbm_mdio);
__raw_writeq(bits | mac_mdio_genc, s->sbm_mdio);
}
}
/**********************************************************************
* SBMAC_MII_SENDDATA(s,data,bitcnt)
*
* Send some bits to the MII. The bits to be sent are right-
* justified in the 'data' parameter.
*
* Input parameters:
* s - sbmac structure
* data - data to send
* bitcnt - number of bits to send
********************************************************************* */
static void sbmac_mii_senddata(struct sbmac_softc *s,unsigned int data, int bitcnt)
{
int i;
uint64_t bits;
unsigned int curmask;
int mac_mdio_genc;
mac_mdio_genc = __raw_readq(s->sbm_mdio) & M_MAC_GENC;
bits = M_MAC_MDIO_DIR_OUTPUT;
__raw_writeq(bits | mac_mdio_genc, s->sbm_mdio);
curmask = 1 << (bitcnt - 1);
for (i = 0; i < bitcnt; i++) {
if (data & curmask)
bits |= M_MAC_MDIO_OUT;
else bits &= ~M_MAC_MDIO_OUT;
__raw_writeq(bits | mac_mdio_genc, s->sbm_mdio);
__raw_writeq(bits | M_MAC_MDC | mac_mdio_genc, s->sbm_mdio);
__raw_writeq(bits | mac_mdio_genc, s->sbm_mdio);
curmask >>= 1;
}
}
/**********************************************************************
* SBMAC_MII_READ(s,phyaddr,regidx)
*
* Read a PHY register.
*
* Input parameters:
* s - sbmac structure
* phyaddr - PHY's address
* regidx = index of register to read
*
* Return value:
* value read, or 0 if an error occurred.
********************************************************************* */
static unsigned int sbmac_mii_read(struct sbmac_softc *s,int phyaddr,int regidx)
{
int idx;
int error;
int regval;
int mac_mdio_genc;
/*
* Synchronize ourselves so that the PHY knows the next
* thing coming down is a command
*/
sbmac_mii_sync(s);
/*
* Send the data to the PHY. The sequence is
* a "start" command (2 bits)
* a "read" command (2 bits)
* the PHY addr (5 bits)
* the register index (5 bits)
*/
sbmac_mii_senddata(s,MII_COMMAND_START, 2);
sbmac_mii_senddata(s,MII_COMMAND_READ, 2);
sbmac_mii_senddata(s,phyaddr, 5);
sbmac_mii_senddata(s,regidx, 5);
mac_mdio_genc = __raw_readq(s->sbm_mdio) & M_MAC_GENC;
/*
* Switch the port around without a clock transition.
*/
__raw_writeq(M_MAC_MDIO_DIR_INPUT | mac_mdio_genc, s->sbm_mdio);
/*
* Send out a clock pulse to signal we want the status
*/
__raw_writeq(M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc, s->sbm_mdio);
__raw_writeq(M_MAC_MDIO_DIR_INPUT | mac_mdio_genc, s->sbm_mdio);
/*
* If an error occurred, the PHY will signal '1' back
*/
error = __raw_readq(s->sbm_mdio) & M_MAC_MDIO_IN;
/*
* Issue an 'idle' clock pulse, but keep the direction
* the same.
*/
__raw_writeq(M_MAC_MDIO_DIR_INPUT | M_MAC_MDC | mac_mdio_genc, s->sbm_mdio);
__raw_writeq(M_MAC_MDIO_DIR_INPUT | mac_mdio_genc, s->sbm_mdio);
regval = 0;
for (idx = 0; idx < 16; idx++) {
regval <<= 1;
if (error == 0) {
if (__raw_readq(s->sbm_mdio) & M_MAC_MDIO_IN)
regval |= 1;
}
__raw_writeq(M_MAC_MDIO_DIR_INPUT|M_MAC_MDC | mac_mdio_genc, s->sbm_mdio);
__raw_writeq(M_MAC_MDIO_DIR_INPUT | mac_mdio_genc, s->sbm_mdio);
}
/* Switch back to output */
__raw_writeq(M_MAC_MDIO_DIR_OUTPUT | mac_mdio_genc, s->sbm_mdio);
if (error == 0)
return regval;
return 0;
}
/**********************************************************************
* SBMAC_MII_WRITE(s,phyaddr,regidx,regval)
*
* Write a value to a PHY register.
*
* Input parameters:
* s - sbmac structure
* phyaddr - PHY to use
* regidx - register within the PHY
* regval - data to write to register
*
* Return value:
* nothing
********************************************************************* */
static void sbmac_mii_write(struct sbmac_softc *s,int phyaddr,int regidx,
unsigned int regval)
{
int mac_mdio_genc;
sbmac_mii_sync(s);
sbmac_mii_senddata(s,MII_COMMAND_START,2);
sbmac_mii_senddata(s,MII_COMMAND_WRITE,2);
sbmac_mii_senddata(s,phyaddr, 5);
sbmac_mii_senddata(s,regidx, 5);
sbmac_mii_senddata(s,MII_COMMAND_ACK,2);
sbmac_mii_senddata(s,regval,16);
mac_mdio_genc = __raw_readq(s->sbm_mdio) & M_MAC_GENC;
__raw_writeq(M_MAC_MDIO_DIR_OUTPUT | mac_mdio_genc, s->sbm_mdio);
}
/**********************************************************************
* SBDMA_INITCTX(d,s,chan,txrx,maxdescr)
*
* Initialize a DMA channel context. Since there are potentially
* eight DMA channels per MAC, it's nice to do this in a standard
* way.
*
* Input parameters:
* d - sbmacdma_t structure (DMA channel context)
* s - sbmac_softc structure (pointer to a MAC)
* chan - channel number (0..1 right now)
* txrx - Identifies DMA_TX or DMA_RX for channel direction
* maxdescr - number of descriptors
*
* Return value:
* nothing
********************************************************************* */
static void sbdma_initctx(sbmacdma_t *d,
struct sbmac_softc *s,
int chan,
int txrx,
int maxdescr)
{
/*
* Save away interesting stuff in the structure
*/
d->sbdma_eth = s;
d->sbdma_channel = chan;
d->sbdma_txdir = txrx;
#if 0
/* RMON clearing */
s->sbe_idx =(s->sbm_base - A_MAC_BASE_0)/MAC_SPACING;
#endif
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_BYTES)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_COLLISIONS)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_LATE_COL)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_EX_COL)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_FCS_ERROR)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_ABORT)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_BAD)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_GOOD)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_RUNT)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_TX_OVERSIZE)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_BYTES)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_MCAST)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_BCAST)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_BAD)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_GOOD)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_RUNT)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_OVERSIZE)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_FCS_ERROR)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_LENGTH_ERROR)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_CODE_ERROR)));
__raw_writeq(0, IOADDR(A_MAC_REGISTER(s->sbe_idx, R_MAC_RMON_RX_ALIGN_ERROR)));
/*
* initialize register pointers
*/
d->sbdma_config0 =
s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CONFIG0);
d->sbdma_config1 =
s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CONFIG1);
d->sbdma_dscrbase =
s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_DSCR_BASE);
d->sbdma_dscrcnt =
s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_DSCR_CNT);
d->sbdma_curdscr =
s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CUR_DSCRADDR);
/*
* Allocate memory for the ring
*/
d->sbdma_maxdescr = maxdescr;
d->sbdma_dscrtable = (sbdmadscr_t *)
kmalloc((d->sbdma_maxdescr+1)*sizeof(sbdmadscr_t), GFP_KERNEL);
/*
* The descriptor table must be aligned to at least 16 bytes or the
* MAC will corrupt it.
*/
d->sbdma_dscrtable = (sbdmadscr_t *)
ALIGN((unsigned long)d->sbdma_dscrtable, sizeof(sbdmadscr_t));
memset(d->sbdma_dscrtable,0,d->sbdma_maxdescr*sizeof(sbdmadscr_t));
d->sbdma_dscrtable_end = d->sbdma_dscrtable + d->sbdma_maxdescr;
d->sbdma_dscrtable_phys = virt_to_phys(d->sbdma_dscrtable);
/*
* And context table
*/
d->sbdma_ctxtable = (struct sk_buff **)
kmalloc(d->sbdma_maxdescr*sizeof(struct sk_buff *), GFP_KERNEL);
memset(d->sbdma_ctxtable,0,d->sbdma_maxdescr*sizeof(struct sk_buff *));
#ifdef CONFIG_SBMAC_COALESCE
/*
* Setup Rx/Tx DMA coalescing defaults
*/
if ( int_pktcnt ) {
d->sbdma_int_pktcnt = int_pktcnt;
} else {
d->sbdma_int_pktcnt = 1;
}
if ( int_timeout ) {
d->sbdma_int_timeout = int_timeout;
} else {
d->sbdma_int_timeout = 0;
}
#endif
}
/**********************************************************************
* SBDMA_CHANNEL_START(d)
*
* Initialize the hardware registers for a DMA channel.
*
* Input parameters:
* d - DMA channel to init (context must be previously init'd
* rxtx - DMA_RX or DMA_TX depending on what type of channel
*
* Return value:
* nothing
********************************************************************* */
static void sbdma_channel_start(sbmacdma_t *d, int rxtx )
{
/*
* Turn on the DMA channel
*/
#ifdef CONFIG_SBMAC_COALESCE
__raw_writeq(V_DMA_INT_TIMEOUT(d->sbdma_int_timeout) |
0, d->sbdma_config1);
__raw_writeq(M_DMA_EOP_INT_EN |
V_DMA_RINGSZ(d->sbdma_maxdescr) |
V_DMA_INT_PKTCNT(d->sbdma_int_pktcnt) |
0, d->sbdma_config0);
#else
__raw_writeq(0, d->sbdma_config1);
__raw_writeq(V_DMA_RINGSZ(d->sbdma_maxdescr) |
0, d->sbdma_config0);
#endif
__raw_writeq(d->sbdma_dscrtable_phys, d->sbdma_dscrbase);
/*
* Initialize ring pointers
*/
d->sbdma_addptr = d->sbdma_dscrtable;
d->sbdma_remptr = d->sbdma_dscrtable;
}
/**********************************************************************
* SBDMA_CHANNEL_STOP(d)
*
* Initialize the hardware registers for a DMA channel.
*
* Input parameters:
* d - DMA channel to init (context must be previously init'd
*
* Return value:
* nothing
********************************************************************* */
static void sbdma_channel_stop(sbmacdma_t *d)
{
/*
* Turn off the DMA channel
*/
__raw_writeq(0, d->sbdma_config1);
__raw_writeq(0, d->sbdma_dscrbase);
__raw_writeq(0, d->sbdma_config0);
/*
* Zero ring pointers
*/
d->sbdma_addptr = NULL;
d->sbdma_remptr = NULL;
}
static void sbdma_align_skb(struct sk_buff *skb,int power2,int offset)
{
unsigned long addr;
unsigned long newaddr;
addr = (unsigned long) skb->data;
newaddr = (addr + power2 - 1) & ~(power2 - 1);
skb_reserve(skb,newaddr-addr+offset);
}
/**********************************************************************
* SBDMA_ADD_RCVBUFFER(d,sb)
*
* Add a buffer to the specified DMA channel. For receive channels,
* this queues a buffer for inbound packets.
*
* Input parameters:
* d - DMA channel descriptor
* sb - sk_buff to add, or NULL if we should allocate one
*
* Return value:
* 0 if buffer could not be added (ring is full)
* 1 if buffer added successfully
********************************************************************* */
static int sbdma_add_rcvbuffer(sbmacdma_t *d,struct sk_buff *sb)
{
sbdmadscr_t *dsc;
sbdmadscr_t *nextdsc;
struct sk_buff *sb_new = NULL;
int pktsize = ENET_PACKET_SIZE;
/* get pointer to our current place in the ring */
dsc = d->sbdma_addptr;
nextdsc = SBDMA_NEXTBUF(d,sbdma_addptr);
/*
* figure out if the ring is full - if the next descriptor
* is the same as the one that we're going to remove from
* the ring, the ring is full
*/
if (nextdsc == d->sbdma_remptr) {
return -ENOSPC;
}
/*
* Allocate a sk_buff if we don't already have one.
* If we do have an sk_buff, reset it so that it's empty.
*
* Note: sk_buffs don't seem to be guaranteed to have any sort
* of alignment when they are allocated. Therefore, allocate enough
* extra space to make sure that:
*
* 1. the data does not start in the middle of a cache line.
* 2. The data does not end in the middle of a cache line
* 3. The buffer can be aligned such that the IP addresses are
* naturally aligned.
*
* Remember, the SOCs MAC writes whole cache lines at a time,
* without reading the old contents first. So, if the sk_buff's
* data portion starts in the middle of a cache line, the SOC
* DMA will trash the beginning (and ending) portions.
*/
if (sb == NULL) {
sb_new = dev_alloc_skb(ENET_PACKET_SIZE + SMP_CACHE_BYTES * 2 + ETHER_ALIGN);
if (sb_new == NULL) {
printk(KERN_INFO "%s: sk_buff allocation failed\n",
d->sbdma_eth->sbm_dev->name);
return -ENOBUFS;
}
sbdma_align_skb(sb_new, SMP_CACHE_BYTES, ETHER_ALIGN);
/* mark skbuff owned by our device */
sb_new->dev = d->sbdma_eth->sbm_dev;
}
else {
sb_new = sb;
/*
* nothing special to reinit buffer, it's already aligned
* and sb->data already points to a good place.
*/
}
/*
* fill in the descriptor
*/
#ifdef CONFIG_SBMAC_COALESCE
/*
* Do not interrupt per DMA transfer.
*/
dsc->dscr_a = virt_to_phys(sb_new->data) |
V_DMA_DSCRA_A_SIZE(NUMCACHEBLKS(pktsize+ETHER_ALIGN)) | 0;
#else
dsc->dscr_a = virt_to_phys(sb_new->data) |
V_DMA_DSCRA_A_SIZE(NUMCACHEBLKS(pktsize+ETHER_ALIGN)) |
M_DMA_DSCRA_INTERRUPT;
#endif
/* receiving: no options */
dsc->dscr_b = 0;
/*
* fill in the context
*/
d->sbdma_ctxtable[dsc-d->sbdma_dscrtable] = sb_new;
/*
* point at next packet
*/
d->sbdma_addptr = nextdsc;
/*
* Give the buffer to the DMA engine.
*/
__raw_writeq(1, d->sbdma_dscrcnt);
return 0; /* we did it */
}
/**********************************************************************
* SBDMA_ADD_TXBUFFER(d,sb)
*
* Add a transmit buffer to the specified DMA channel, causing a
* transmit to start.
*
* Input parameters:
* d - DMA channel descriptor
* sb - sk_buff to add
*
* Return value:
* 0 transmit queued successfully
* otherwise error code
********************************************************************* */
static int sbdma_add_txbuffer(sbmacdma_t *d,struct sk_buff *sb)
{
sbdmadscr_t *dsc;
sbdmadscr_t *nextdsc;
uint64_t phys;
uint64_t ncb;
int length;
/* get pointer to our current place in the ring */