summaryrefslogtreecommitdiffstats
path: root/src/drivers/net/natsemi.c
diff options
context:
space:
mode:
authorUdayan Kumar2007-07-10 21:08:58 +0200
committerUdayan Kumar2007-07-15 03:08:06 +0200
commit334abbde83749baf9813395a2ee1c97f9a676c4c (patch)
tree1d690072db091a1c547172d31c98632ccab10750 /src/drivers/net/natsemi.c
parentadded cable magic for 100Mps in natsemi (diff)
downloadipxe-334abbde83749baf9813395a2ee1c97f9a676c4c.tar.gz
ipxe-334abbde83749baf9813395a2ee1c97f9a676c4c.tar.xz
ipxe-334abbde83749baf9813395a2ee1c97f9a676c4c.zip
init_fix_up
Diffstat (limited to 'src/drivers/net/natsemi.c')
-rw-r--r--src/drivers/net/natsemi.c161
1 files changed, 161 insertions, 0 deletions
diff --git a/src/drivers/net/natsemi.c b/src/drivers/net/natsemi.c
index 43b7f2df..e69a8f3f 100644
--- a/src/drivers/net/natsemi.c
+++ b/src/drivers/net/natsemi.c
@@ -79,6 +79,7 @@
#include <gpxe/spi_bit.h>
#include <gpxe/threewire.h>
#include <gpxe/nvo.h>
+#include <mii.h>
#define TX_RING_SIZE 4
#define NUM_RX_DESC 4
@@ -122,6 +123,28 @@ struct natsemi_nic {
struct nvo_block nvo;
};
+
+/*
+ * Support for fibre connections on Am79C874:
+ * This phy needs a special setup when connected to a fibre cable.
+ * http://www.amd.com/files/connectivitysolutions/networking/archivednetworking/22235.pdf
+ */
+#define PHYID_AM79C874 0x0022561b
+
+enum {
+ MII_MCTRL = 0x15, /* mode control register */
+ MII_FX_SEL = 0x0001, /* 100BASE-FX (fiber) */
+ MII_EN_SCRM = 0x0004, /* enable scrambler (tp) */
+};
+
+
+
+/* values we might find in the silicon revision register */
+#define SRR_DP83815_C 0x0302
+#define SRR_DP83815_D 0x0403
+#define SRR_DP83816_A4 0x0504
+#define SRR_DP83816_A5 0x0505
+
/* NATSEMI: Offsets to the device registers.
* Unlike software-only systems, device drivers interact with complex hardware.
* 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 ) {
outl ( SavedClkRun, nat->ioaddr + ClkRun );
}
+
+static int mdio_read(struct net_device *netdev, int reg) {
+ struct natsemi_nic *nat = netdev->priv;
+
+ /* The 83815 series has two ports:
+ * - an internal transceiver
+ * - an external mii bus
+ */
+ return inw(nat->ioaddr+BasicControl+(reg<<2));
+}
+
+static void mdio_write(struct net_device *netdev, int reg, u16 data) {
+ struct natsemi_nic *nat = netdev->priv;
+
+ /* The 83815 series has an internal transceiver; handle separately */
+ writew(data, nat->ioaddr+BasicControl+(reg<<2));
+}
+
+static void init_phy_fixup(struct net_device *netdev) {
+ struct natsemi_nic *nat = netdev->priv;
+ int i;
+ u32 cfg;
+ u16 tmp;
+ uint16_t advertising;
+ int mii;
+
+ /* restore stuff lost when power was out */
+ tmp = mdio_read(netdev, MII_BMCR);
+ advertising= mdio_read(netdev, MII_ADVERTISE);
+// if (np->autoneg == AUTONEG_ENABLE) {
+ /* renegotiate if something changed */
+ if ((tmp & BMCR_ANENABLE) == 0
+ || advertising != mdio_read(netdev, MII_ADVERTISE))
+ {
+ /* turn on autonegotiation and force negotiation */
+ tmp |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ mdio_write(netdev, MII_ADVERTISE, advertising);
+ }
+// } else {
+ /* turn off auto negotiation, set speed and duplexity */
+// tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
+// if (np->speed == SPEED_100)
+/// tmp |= BMCR_SPEED100;
+// if (np->duplex == DUPLEX_FULL)
+// tmp |= BMCR_FULLDPLX;
+ /*
+ * Note: there is no good way to inform the link partner
+ * that our capabilities changed. The user has to unplug
+ * and replug the network cable after some changes, e.g.
+ * after switching from 10HD, autoneg off to 100 HD,
+ * autoneg off.
+ */
+// }
+ mdio_write(netdev, MII_BMCR, tmp);
+ inl(nat->ioaddr + ChipConfig);
+ udelay(1);
+
+ /* find out what phy this is */
+ mii = (mdio_read(netdev, MII_PHYSID1) << 16)
+ + mdio_read(netdev, MII_PHYSID2);
+
+ /* handle external phys here */
+ switch (mii) {
+ case PHYID_AM79C874:
+ /* phy specific configuration for fibre/tp operation */
+ tmp = mdio_read(netdev, MII_MCTRL);
+ tmp &= ~(MII_FX_SEL | MII_EN_SCRM);
+ //if (dev->if_port == PORT_FIBRE)
+ // tmp |= MII_FX_SEL;
+ //else
+ tmp |= MII_EN_SCRM;
+ mdio_write(netdev, MII_MCTRL, tmp);
+ break;
+ default:
+ break;
+ }
+ cfg = inl(nat->ioaddr + ChipConfig);
+ if (cfg & CfgExtPhy)
+ return;
+
+ /* On page 78 of the spec, they recommend some settings for "optimum
+ performance" to be done in sequence. These settings optimize some
+ of the 100Mbit autodetection circuitry. They say we only want to
+ do this for rev C of the chip, but engineers at NSC (Bradley
+ Kennedy) recommends always setting them. If you don't, you get
+ errors on some autonegotiations that make the device unusable.
+
+ It seems that the DSP needs a few usec to reinitialize after
+ the start of the phy. Just retry writing these values until they
+ stick.
+ */
+ uint32_t srr = inl(nat->ioaddr + SiliconRev);
+ int NATSEMI_HW_TIMEOUT = 400;
+ for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+
+ int dspcfg,dspcfg_1;
+ outw(1, nat->ioaddr + PGSEL);
+ outw(PMDCSR_VAL, nat->ioaddr + PMDCSR);
+ outw(TSTDAT_VAL, nat->ioaddr + TSTDAT);
+ dspcfg = (srr <= SRR_DP83815_C)?
+ DSPCFG_VAL : (DSPCFG_COEF | readw(nat->ioaddr + DSPCFG));
+ outw(dspcfg, nat->ioaddr + DSPCFG);
+ outw(SDCFG_VAL, nat->ioaddr + SDCFG);
+ outw(0, nat->ioaddr + PGSEL);
+ inl(nat->ioaddr + ChipConfig);
+ udelay(10);
+
+ outw(1, nat->ioaddr + PGSEL);
+ dspcfg_1 = readw(nat->ioaddr + DSPCFG);
+ outw(0, nat->ioaddr + PGSEL);
+ if (dspcfg == dspcfg_1)
+ break;
+ }
+
+ if (i==NATSEMI_HW_TIMEOUT) {
+ DBG ( "Natsemi: DSPCFG mismatch after retrying for"
+ " %d usec.\n", i*10);
+ } else {
+ DBG ( "NATSEMI: DSPCFG accepted after %d usec.\n",
+ i*10);
+ }
+ /*
+ * Enable PHY Specific event based interrupts. Link state change
+ * and Auto-Negotiation Completion are among the affected.
+ * Read the intr status to clear it (needed for wake events).
+ */
+ inw(nat->ioaddr + MIntrStatus);
+ //MICRIntEn = 0x2
+ outw(0x2, nat->ioaddr + MIntrCtrl);
+}
+
+
+/*
+ * Patch up for fixing CRC errors.
+ * adapted from linux natsemi driver
+ *
+ */
static void do_cable_magic ( struct net_device *netdev ) {
struct natsemi_nic *nat = netdev->priv;
uint16_t data;
@@ -478,6 +638,7 @@ static int nat_open ( struct net_device *netdev ) {
* testing this feature is required or not
*/
do_cable_magic ( netdev );
+ init_phy_fixup ( netdev );
/* mask the interrupts. note interrupt is not enabled here