mirror of https://github.com/ipxe/ipxe.git
init_fix_up
parent
751cb2e450
commit
334abbde83
|
@ -79,6 +79,7 @@
|
||||||
#include <gpxe/spi_bit.h>
|
#include <gpxe/spi_bit.h>
|
||||||
#include <gpxe/threewire.h>
|
#include <gpxe/threewire.h>
|
||||||
#include <gpxe/nvo.h>
|
#include <gpxe/nvo.h>
|
||||||
|
#include <mii.h>
|
||||||
|
|
||||||
#define TX_RING_SIZE 4
|
#define TX_RING_SIZE 4
|
||||||
#define NUM_RX_DESC 4
|
#define NUM_RX_DESC 4
|
||||||
|
@ -122,6 +123,28 @@ struct natsemi_nic {
|
||||||
struct nvo_block nvo;
|
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.
|
/* NATSEMI: Offsets to the device registers.
|
||||||
* Unlike software-only systems, device drivers interact with complex hardware.
|
* Unlike software-only systems, device drivers interact with complex hardware.
|
||||||
* It's not useful to define symbolic names for every register bit in the
|
* 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 );
|
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 ) {
|
static void do_cable_magic ( struct net_device *netdev ) {
|
||||||
struct natsemi_nic *nat = netdev->priv;
|
struct natsemi_nic *nat = netdev->priv;
|
||||||
uint16_t data;
|
uint16_t data;
|
||||||
|
@ -478,6 +638,7 @@ static int nat_open ( struct net_device *netdev ) {
|
||||||
* testing this feature is required or not
|
* testing this feature is required or not
|
||||||
*/
|
*/
|
||||||
do_cable_magic ( netdev );
|
do_cable_magic ( netdev );
|
||||||
|
init_phy_fixup ( netdev );
|
||||||
|
|
||||||
|
|
||||||
/* mask the interrupts. note interrupt is not enabled here
|
/* mask the interrupts. note interrupt is not enabled here
|
||||||
|
|
Loading…
Reference in New Issue