|
@@ -79,6 +79,7 @@
|
79
|
79
|
#include <gpxe/spi_bit.h>
|
80
|
80
|
#include <gpxe/threewire.h>
|
81
|
81
|
#include <gpxe/nvo.h>
|
|
82
|
+#include <mii.h>
|
82
|
83
|
|
83
|
84
|
#define TX_RING_SIZE 4
|
84
|
85
|
#define NUM_RX_DESC 4
|
|
@@ -122,6 +123,28 @@ struct natsemi_nic {
|
122
|
123
|
struct nvo_block nvo;
|
123
|
124
|
};
|
124
|
125
|
|
|
126
|
+
|
|
127
|
+/*
|
|
128
|
+ * Support for fibre connections on Am79C874:
|
|
129
|
+ * This phy needs a special setup when connected to a fibre cable.
|
|
130
|
+ * http://www.amd.com/files/connectivitysolutions/networking/archivednetworking/22235.pdf
|
|
131
|
+ */
|
|
132
|
+#define PHYID_AM79C874 0x0022561b
|
|
133
|
+
|
|
134
|
+enum {
|
|
135
|
+ MII_MCTRL = 0x15, /* mode control register */
|
|
136
|
+ MII_FX_SEL = 0x0001, /* 100BASE-FX (fiber) */
|
|
137
|
+ MII_EN_SCRM = 0x0004, /* enable scrambler (tp) */
|
|
138
|
+};
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+/* values we might find in the silicon revision register */
|
|
143
|
+#define SRR_DP83815_C 0x0302
|
|
144
|
+#define SRR_DP83815_D 0x0403
|
|
145
|
+#define SRR_DP83816_A4 0x0504
|
|
146
|
+#define SRR_DP83816_A5 0x0505
|
|
147
|
+
|
125
|
148
|
/* NATSEMI: Offsets to the device registers.
|
126
|
149
|
* Unlike software-only systems, device drivers interact with complex hardware.
|
127
|
150
|
* It's not useful to define symbolic names for every register bit in the
|
|
@@ -351,6 +374,143 @@ static void nat_reset ( struct natsemi_nic *nat ) {
|
351
|
374
|
outl ( SavedClkRun, nat->ioaddr + ClkRun );
|
352
|
375
|
}
|
353
|
376
|
|
|
377
|
+
|
|
378
|
+static int mdio_read(struct net_device *netdev, int reg) {
|
|
379
|
+ struct natsemi_nic *nat = netdev->priv;
|
|
380
|
+
|
|
381
|
+ /* The 83815 series has two ports:
|
|
382
|
+ * - an internal transceiver
|
|
383
|
+ * - an external mii bus
|
|
384
|
+ */
|
|
385
|
+ return inw(nat->ioaddr+BasicControl+(reg<<2));
|
|
386
|
+}
|
|
387
|
+
|
|
388
|
+static void mdio_write(struct net_device *netdev, int reg, u16 data) {
|
|
389
|
+ struct natsemi_nic *nat = netdev->priv;
|
|
390
|
+
|
|
391
|
+ /* The 83815 series has an internal transceiver; handle separately */
|
|
392
|
+ writew(data, nat->ioaddr+BasicControl+(reg<<2));
|
|
393
|
+}
|
|
394
|
+
|
|
395
|
+static void init_phy_fixup(struct net_device *netdev) {
|
|
396
|
+ struct natsemi_nic *nat = netdev->priv;
|
|
397
|
+ int i;
|
|
398
|
+ u32 cfg;
|
|
399
|
+ u16 tmp;
|
|
400
|
+ uint16_t advertising;
|
|
401
|
+ int mii;
|
|
402
|
+
|
|
403
|
+ /* restore stuff lost when power was out */
|
|
404
|
+ tmp = mdio_read(netdev, MII_BMCR);
|
|
405
|
+ advertising= mdio_read(netdev, MII_ADVERTISE);
|
|
406
|
+// if (np->autoneg == AUTONEG_ENABLE) {
|
|
407
|
+ /* renegotiate if something changed */
|
|
408
|
+ if ((tmp & BMCR_ANENABLE) == 0
|
|
409
|
+ || advertising != mdio_read(netdev, MII_ADVERTISE))
|
|
410
|
+ {
|
|
411
|
+ /* turn on autonegotiation and force negotiation */
|
|
412
|
+ tmp |= (BMCR_ANENABLE | BMCR_ANRESTART);
|
|
413
|
+ mdio_write(netdev, MII_ADVERTISE, advertising);
|
|
414
|
+ }
|
|
415
|
+// } else {
|
|
416
|
+ /* turn off auto negotiation, set speed and duplexity */
|
|
417
|
+// tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
|
|
418
|
+// if (np->speed == SPEED_100)
|
|
419
|
+/// tmp |= BMCR_SPEED100;
|
|
420
|
+// if (np->duplex == DUPLEX_FULL)
|
|
421
|
+// tmp |= BMCR_FULLDPLX;
|
|
422
|
+ /*
|
|
423
|
+ * Note: there is no good way to inform the link partner
|
|
424
|
+ * that our capabilities changed. The user has to unplug
|
|
425
|
+ * and replug the network cable after some changes, e.g.
|
|
426
|
+ * after switching from 10HD, autoneg off to 100 HD,
|
|
427
|
+ * autoneg off.
|
|
428
|
+ */
|
|
429
|
+// }
|
|
430
|
+ mdio_write(netdev, MII_BMCR, tmp);
|
|
431
|
+ inl(nat->ioaddr + ChipConfig);
|
|
432
|
+ udelay(1);
|
|
433
|
+
|
|
434
|
+ /* find out what phy this is */
|
|
435
|
+ mii = (mdio_read(netdev, MII_PHYSID1) << 16)
|
|
436
|
+ + mdio_read(netdev, MII_PHYSID2);
|
|
437
|
+
|
|
438
|
+ /* handle external phys here */
|
|
439
|
+ switch (mii) {
|
|
440
|
+ case PHYID_AM79C874:
|
|
441
|
+ /* phy specific configuration for fibre/tp operation */
|
|
442
|
+ tmp = mdio_read(netdev, MII_MCTRL);
|
|
443
|
+ tmp &= ~(MII_FX_SEL | MII_EN_SCRM);
|
|
444
|
+ //if (dev->if_port == PORT_FIBRE)
|
|
445
|
+ // tmp |= MII_FX_SEL;
|
|
446
|
+ //else
|
|
447
|
+ tmp |= MII_EN_SCRM;
|
|
448
|
+ mdio_write(netdev, MII_MCTRL, tmp);
|
|
449
|
+ break;
|
|
450
|
+ default:
|
|
451
|
+ break;
|
|
452
|
+ }
|
|
453
|
+ cfg = inl(nat->ioaddr + ChipConfig);
|
|
454
|
+ if (cfg & CfgExtPhy)
|
|
455
|
+ return;
|
|
456
|
+
|
|
457
|
+ /* On page 78 of the spec, they recommend some settings for "optimum
|
|
458
|
+ performance" to be done in sequence. These settings optimize some
|
|
459
|
+ of the 100Mbit autodetection circuitry. They say we only want to
|
|
460
|
+ do this for rev C of the chip, but engineers at NSC (Bradley
|
|
461
|
+ Kennedy) recommends always setting them. If you don't, you get
|
|
462
|
+ errors on some autonegotiations that make the device unusable.
|
|
463
|
+
|
|
464
|
+ It seems that the DSP needs a few usec to reinitialize after
|
|
465
|
+ the start of the phy. Just retry writing these values until they
|
|
466
|
+ stick.
|
|
467
|
+ */
|
|
468
|
+ uint32_t srr = inl(nat->ioaddr + SiliconRev);
|
|
469
|
+ int NATSEMI_HW_TIMEOUT = 400;
|
|
470
|
+ for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
|
|
471
|
+
|
|
472
|
+ int dspcfg,dspcfg_1;
|
|
473
|
+ outw(1, nat->ioaddr + PGSEL);
|
|
474
|
+ outw(PMDCSR_VAL, nat->ioaddr + PMDCSR);
|
|
475
|
+ outw(TSTDAT_VAL, nat->ioaddr + TSTDAT);
|
|
476
|
+ dspcfg = (srr <= SRR_DP83815_C)?
|
|
477
|
+ DSPCFG_VAL : (DSPCFG_COEF | readw(nat->ioaddr + DSPCFG));
|
|
478
|
+ outw(dspcfg, nat->ioaddr + DSPCFG);
|
|
479
|
+ outw(SDCFG_VAL, nat->ioaddr + SDCFG);
|
|
480
|
+ outw(0, nat->ioaddr + PGSEL);
|
|
481
|
+ inl(nat->ioaddr + ChipConfig);
|
|
482
|
+ udelay(10);
|
|
483
|
+
|
|
484
|
+ outw(1, nat->ioaddr + PGSEL);
|
|
485
|
+ dspcfg_1 = readw(nat->ioaddr + DSPCFG);
|
|
486
|
+ outw(0, nat->ioaddr + PGSEL);
|
|
487
|
+ if (dspcfg == dspcfg_1)
|
|
488
|
+ break;
|
|
489
|
+ }
|
|
490
|
+
|
|
491
|
+ if (i==NATSEMI_HW_TIMEOUT) {
|
|
492
|
+ DBG ( "Natsemi: DSPCFG mismatch after retrying for"
|
|
493
|
+ " %d usec.\n", i*10);
|
|
494
|
+ } else {
|
|
495
|
+ DBG ( "NATSEMI: DSPCFG accepted after %d usec.\n",
|
|
496
|
+ i*10);
|
|
497
|
+ }
|
|
498
|
+ /*
|
|
499
|
+ * Enable PHY Specific event based interrupts. Link state change
|
|
500
|
+ * and Auto-Negotiation Completion are among the affected.
|
|
501
|
+ * Read the intr status to clear it (needed for wake events).
|
|
502
|
+ */
|
|
503
|
+ inw(nat->ioaddr + MIntrStatus);
|
|
504
|
+ //MICRIntEn = 0x2
|
|
505
|
+ outw(0x2, nat->ioaddr + MIntrCtrl);
|
|
506
|
+}
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+/*
|
|
510
|
+ * Patch up for fixing CRC errors.
|
|
511
|
+ * adapted from linux natsemi driver
|
|
512
|
+ *
|
|
513
|
+ */
|
354
|
514
|
static void do_cable_magic ( struct net_device *netdev ) {
|
355
|
515
|
struct natsemi_nic *nat = netdev->priv;
|
356
|
516
|
uint16_t data;
|
|
@@ -478,6 +638,7 @@ static int nat_open ( struct net_device *netdev ) {
|
478
|
638
|
* testing this feature is required or not
|
479
|
639
|
*/
|
480
|
640
|
do_cable_magic ( netdev );
|
|
641
|
+ init_phy_fixup ( netdev );
|
481
|
642
|
|
482
|
643
|
|
483
|
644
|
/* mask the interrupts. note interrupt is not enabled here
|