/* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT ((800*HZ)/1000)
/* To minimize the size of the driver source and make the driver more readablenotallconstantsaresymbolicallydefined.
You'll need the manual if you want to understand driver details anyway. */ /* Offsets from base I/O address. */ #define EL3_DATA 0x00 #define EL3_CMD 0x0e #define EL3_STATUS 0x0e
#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
struct el3_private { struct pcmcia_device *p_dev;
u16 advertising, partner; /* NWay media advertisement */ unsignedchar phys; /* MII device address */ unsignedint autoselect:1, default_media:3; /* Read from the EEPROM/Wn3_Config. */ /* for transceiver monitoring */ struct timer_list media; unsignedshort media_status; unsignedshort fast_poll; unsignedlong last_irq;
spinlock_t window_lock; /* Guards the Window selection */
};
/* Set iff a MII transceiver on any interface requires mdio preamble. ThisonlysetwiththeoriginalDP83840onolder3c905boards,sotheextra
code size of a per-interface flag is not worthwhile. */ staticchar mii_preamble_required = 0;
staticvoid mdio_sync(unsignedint ioaddr, int bits); staticint mdio_read(unsignedint ioaddr, int phy_id, int location); staticvoid mdio_write(unsignedint ioaddr, int phy_id, int location, int value); staticunsignedshort read_eeprom(unsignedint ioaddr, int index); staticvoid tc574_wait_for_completion(struct net_device *dev, int cmd);
for (i = j = 0; j < 0x400; j += 0x20) {
link->resource[0]->start = j ^ 0x300;
i = pcmcia_request_io(link); if (i == 0) break;
} if (i != 0) goto failed;
ret = pcmcia_request_irq(link, el3_interrupt); if (ret) goto failed;
ret = pcmcia_enable_device(link); if (ret) goto failed;
/* The 3c574 normally uses an EEPROM for configuration info, including thehardwareaddress.Thefutureproductsmayincludeamodemchip
and put the address in the CIS. */
len = pcmcia_get_tuple(link, 0x88, &buf); if (buf && len >= 6) { for (i = 0; i < 3; i++)
addr[i] = htons(le16_to_cpu(buf[i * 2]));
kfree(buf);
} else {
kfree(buf); /* 0 < len < 6 */
EL3WINDOW(0); for (i = 0; i < 3; i++)
addr[i] = htons(read_eeprom(ioaddr, i + 10)); if (addr[0] == htons(0x6060)) {
pr_notice("IO port conflict at 0x%03lx-0x%03lx\n",
dev->base_addr, dev->base_addr+15); goto failed;
}
}
eth_hw_addr_set(dev, (u8 *)addr); if (link->prod_id[1])
cardname = link->prod_id[1]; else
cardname = "3Com 3c574";
if (link->open) {
tc574_reset(dev);
netif_device_attach(dev);
}
return0;
}
staticvoid dump_status(struct net_device *dev)
{ unsignedint ioaddr = dev->base_addr;
EL3WINDOW(1);
netdev_info(dev, " irq status %04x, rx status %04x, tx status %02x, tx free %04x\n",
inw(ioaddr+EL3_STATUS),
inw(ioaddr+RxStatus), inb(ioaddr+TxStatus),
inw(ioaddr+TxFree));
EL3WINDOW(4);
netdev_info(dev, " diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
inw(ioaddr+0x04), inw(ioaddr+0x06),
inw(ioaddr+0x08), inw(ioaddr+0x0a));
EL3WINDOW(1);
}
/* Usethisforcommandsthatmaytaketimetofinish
*/ staticvoid tc574_wait_for_completion(struct net_device *dev, int cmd)
{ int i = 1500;
outw(cmd, dev->base_addr + EL3_CMD); while (--i > 0) if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break; if (i == 0)
netdev_notice(dev, "command 0x%04x did not complete!\n", cmd);
}
/* Read a word from the EEPROM using the regular EEPROM access register. Assumethatweareinregisterwindowzero.
*/ staticunsignedshort read_eeprom(unsignedint ioaddr, int index)
{ int timer;
outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd); /* Pause for at least 162 usec for the read to take place. */ for (timer = 1620; timer >= 0; timer--) { if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) break;
} return inw(ioaddr + Wn0EepromData);
}
/* MII transceiver control section. ReadandwritetheMIIregistersusingsoftware-generatedserial MDIOprotocol.SeetheMIIspecificationsorDP83840Adatasheet fordetails. Themaxiumdataclockrateis2.5Mhz.Thetimingiseasilymetbythe
slow PC card interface. */
/* Generate the preamble required for initial synchronization and
a few older transceivers. */ staticvoid mdio_sync(unsignedint ioaddr, int bits)
{ unsignedint mdio_addr = ioaddr + Wn4_PhysicalMgmt;
/* Establish sync by sending at least 32 logic ones. */ while (-- bits >= 0) {
outw(MDIO_DATA_WRITE1, mdio_addr);
outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
}
}
staticint mdio_read(unsignedint ioaddr, int phy_id, int location)
{ int i; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; unsignedint retval = 0; unsignedint mdio_addr = ioaddr + Wn4_PhysicalMgmt;
if (mii_preamble_required)
mdio_sync(ioaddr, 32);
/* Shift the read command bits out. */ for (i = 14; i >= 0; i--) { int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
outw(dataval, mdio_addr);
outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
} /* Read the two transition, 16 data, and wire-idle bits. */ for (i = 19; i > 0; i--) {
outw(MDIO_ENB_IN, mdio_addr);
retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
} return (retval>>1) & 0xffff;
}
staticvoid mdio_write(unsignedint ioaddr, int phy_id, int location, int value)
{ int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value; unsignedint mdio_addr = ioaddr + Wn4_PhysicalMgmt; int i;
if (mii_preamble_required)
mdio_sync(ioaddr, 32);
/* Shift the command bits out. */ for (i = 31; i >= 0; i--) { int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
outw(dataval, mdio_addr);
outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
} /* Leave the interface idle. */ for (i = 1; i >= 0; i--) {
outw(MDIO_ENB_IN, mdio_addr);
outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
}
}
/* Reset and restore all of the 3c574 registers. */ staticvoid tc574_reset(struct net_device *dev)
{ struct el3_private *lp = netdev_priv(dev); int i; unsignedint ioaddr = dev->base_addr; unsignedlong flags;
tc574_wait_for_completion(dev, TotalReset|0x10);
spin_lock_irqsave(&lp->window_lock, flags); /* Clear any transactions in progress. */
outw(0, ioaddr + RunnerWrCtrl);
outw(0, ioaddr + RunnerRdCtrl);
/* Set the station address and mask. */
EL3WINDOW(2); for (i = 0; i < 6; i++)
outb(dev->dev_addr[i], ioaddr + i); for (; i < 12; i+=2)
outw(0, ioaddr + i);
/* .. re-sync MII and re-fill what NWay is advertising. */
mdio_sync(ioaddr, 32);
mdio_write(ioaddr, lp->phys, 4, lp->advertising); if (!auto_polarity) { /* works for TDK 78Q2120 series MII's */
i = mdio_read(ioaddr, lp->phys, 16) | 0x20;
mdio_write(ioaddr, lp->phys, 16, i);
}
spin_lock_irqsave(&lp->window_lock, flags); /* Switch to register set 1 for normal use, just for TxFree. */
set_rx_mode(dev);
spin_unlock_irqrestore(&lp->window_lock, flags);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ /* Allow status bits to be seen. */
outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD); /* Ack all pending events, and set active indicator mask. */
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
ioaddr + EL3_CMD);
outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
| AdapterFailure | RxEarly, ioaddr + EL3_CMD);
}
/* Put out the doubleword header... */
outw(skb->len, ioaddr + TX_FIFO);
outw(0, ioaddr + TX_FIFO); /* ... and the packet rounded to a doubleword. */
outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
/* TxFree appears only in Window 1, not offset 0x1c. */ if (inw(ioaddr + TxFree) <= 1536) {
netif_stop_queue(dev); /* Interrupt us when the FIFO has room for max-sized packet.
The threshold is in units of dwords. */
outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
}
if (!netif_device_present(dev)) return IRQ_NONE;
ioaddr = dev->base_addr;
pr_debug("%s: interrupt, status %4.4x.\n",
dev->name, inw(ioaddr + EL3_STATUS));
spin_lock(&lp->window_lock);
while ((status = inw(ioaddr + EL3_STATUS)) &
(IntLatch | RxComplete | RxEarly | StatsFull)) { if (!netif_device_present(dev) ||
((status & 0xe000) != 0x2000)) {
pr_debug("%s: Interrupt from dead card\n", dev->name); break;
}
handled = 1;
if (status & RxComplete)
work_budget = el3_rx(dev, work_budget);
if (status & TxAvailable) {
pr_debug(" TX room bit was handled.\n"); /* There's room in the FIFO for a full-sized packet. */
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
netif_wake_queue(dev);
}
/* Check for pending interrupt with expired latency timer: with
this, we can limp along even if the interrupt is blocked */ if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) { if (!lp->fast_poll)
netdev_info(dev, "interrupt(s) dropped!\n");
switch(cmd) { case SIOCGMIIPHY: /* Get the address of the PHY in use. */
data->phy_id = phy;
fallthrough; case SIOCGMIIREG: /* Read the specified MII register. */
{ int saved_window; unsignedlong flags;
spin_lock_irqsave(&lp->window_lock, flags);
saved_window = inw(ioaddr + EL3_CMD) >> 13;
EL3WINDOW(4);
data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f,
data->reg_num & 0x1f);
EL3WINDOW(saved_window);
spin_unlock_irqrestore(&lp->window_lock, flags); return0;
} case SIOCSMIIREG: /* Write the specified MII register */
{ int saved_window; unsignedlong flags;
/* The Odie chip has a 64 bin multicast filter, but the bit layout is not documented.Untilitiswereverttoreceivingallmulticastframeswhen anymulticastreceptionisdesired. Note:Myotherdriversemitalogmessagewheneverpromiscuousmodeis enteredtohelpdetectpasswordsniffers.Thisislessdesirableon typicalPCcardmachines,soweomitthemessage.
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.