|
|
|
@@ -7,7 +7,7 @@
|
|
|
|
|
* Emulation of the AMD PCnet LANCE NIC controller for both the ISA, VLB,
|
|
|
|
|
* and PCI buses.
|
|
|
|
|
*
|
|
|
|
|
* Version: @(#)net_pcnet.c 1.0.0 2019/11/09
|
|
|
|
|
* Version: @(#)net_pcnet.c 1.0.1 2020/03/23
|
|
|
|
|
*
|
|
|
|
|
* Authors: Miran Grca, <mgrca8@gmail.com>
|
|
|
|
|
* TheCollector1995, <mariogplayer@gmail.com>
|
|
|
|
@@ -45,7 +45,6 @@
|
|
|
|
|
#include "net_pcnet.h"
|
|
|
|
|
#include "bswap.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* PCI info. */
|
|
|
|
|
#define PCI_VENDID 0x1022 /* AMD */
|
|
|
|
|
#define PCI_DEVID 0x2000 /* PCnet-PCI II (Am79c970A) */
|
|
|
|
@@ -119,37 +118,37 @@ typedef struct RTNETETHERHDR
|
|
|
|
|
|
|
|
|
|
/** @name Bus configuration sub register accessors.
|
|
|
|
|
* @{ */
|
|
|
|
|
#define BCR_DWIO(S) ((S)->aBCR[BCR_BSBC] & 0x0080)
|
|
|
|
|
#define BCR_SSIZE32(S) ((S)->aBCR[BCR_SWS ] & 0x0100)
|
|
|
|
|
#define BCR_DWIO(S) !!((S)->aBCR[BCR_BSBC] & 0x0080)
|
|
|
|
|
#define BCR_SSIZE32(S) !!((S)->aBCR[BCR_SWS ] & 0x0100)
|
|
|
|
|
#define BCR_SWSTYLE(S) ((S)->aBCR[BCR_SWS ] & 0x00FF)
|
|
|
|
|
/** @} */
|
|
|
|
|
|
|
|
|
|
/** @name CSR subregister accessors.
|
|
|
|
|
* @{ */
|
|
|
|
|
#define CSR_INIT(S) ((S)->aCSR[0] & 0x0001) /**< Init assertion */
|
|
|
|
|
#define CSR_STRT(S) ((S)->aCSR[0] & 0x0002) /**< Start assertion */
|
|
|
|
|
#define CSR_STOP(S) ((S)->aCSR[0] & 0x0004) /**< Stop assertion */
|
|
|
|
|
#define CSR_TDMD(S) ((S)->aCSR[0] & 0x0008) /**< Transmit demand. (perform xmit poll now (readable, settable, not clearable) */
|
|
|
|
|
#define CSR_TXON(S) ((S)->aCSR[0] & 0x0010) /**< Transmit on (readonly) */
|
|
|
|
|
#define CSR_RXON(S) ((S)->aCSR[0] & 0x0020) /**< Receive On */
|
|
|
|
|
#define CSR_INEA(S) ((S)->aCSR[0] & 0x0040) /**< Interrupt Enable */
|
|
|
|
|
#define CSR_LAPPEN(S) ((S)->aCSR[3] & 0x0020) /**< Look Ahead Packet Processing Enable */
|
|
|
|
|
#define CSR_DXSUFLO(S) ((S)->aCSR[3] & 0x0040) /**< Disable Transmit Stop on Underflow error */
|
|
|
|
|
#define CSR_ASTRP_RCV(S) ((S)->aCSR[4] & 0x0400) /**< Auto Strip Receive */
|
|
|
|
|
#define CSR_DPOLL(S) ((S)->aCSR[4] & 0x1000) /**< Disable Transmit Polling */
|
|
|
|
|
#define CSR_SPND(S) ((S)->aCSR[5] & 0x0001) /**< Suspend */
|
|
|
|
|
#define CSR_LTINTEN(S) ((S)->aCSR[5] & 0x4000) /**< Last Transmit Interrupt Enable */
|
|
|
|
|
#define CSR_TOKINTD(S) ((S)->aCSR[5] & 0x8000) /**< Transmit OK Interrupt Disable */
|
|
|
|
|
#define CSR_INIT(S) !!((S)->aCSR[0] & 0x0001) /**< Init assertion */
|
|
|
|
|
#define CSR_STRT(S) !!((S)->aCSR[0] & 0x0002) /**< Start assertion */
|
|
|
|
|
#define CSR_STOP(S) !!((S)->aCSR[0] & 0x0004) /**< Stop assertion */
|
|
|
|
|
#define CSR_TDMD(S) !!((S)->aCSR[0] & 0x0008) /**< Transmit demand. (perform xmit poll now (readable, settable, not clearable) */
|
|
|
|
|
#define CSR_TXON(S) !!((S)->aCSR[0] & 0x0010) /**< Transmit on (readonly) */
|
|
|
|
|
#define CSR_RXON(S) !!((S)->aCSR[0] & 0x0020) /**< Receive On */
|
|
|
|
|
#define CSR_INEA(S) !!((S)->aCSR[0] & 0x0040) /**< Interrupt Enable */
|
|
|
|
|
#define CSR_LAPPEN(S) !!((S)->aCSR[3] & 0x0020) /**< Look Ahead Packet Processing Enable */
|
|
|
|
|
#define CSR_DXSUFLO(S) !!((S)->aCSR[3] & 0x0040) /**< Disable Transmit Stop on Underflow error */
|
|
|
|
|
#define CSR_ASTRP_RCV(S) !!((S)->aCSR[4] & 0x0400) /**< Auto Strip Receive */
|
|
|
|
|
#define CSR_DPOLL(S) !!((S)->aCSR[4] & 0x1000) /**< Disable Transmit Polling */
|
|
|
|
|
#define CSR_SPND(S) !!((S)->aCSR[5] & 0x0001) /**< Suspend */
|
|
|
|
|
#define CSR_LTINTEN(S) !!((S)->aCSR[5] & 0x4000) /**< Last Transmit Interrupt Enable */
|
|
|
|
|
#define CSR_TOKINTD(S) !!((S)->aCSR[5] & 0x8000) /**< Transmit OK Interrupt Disable */
|
|
|
|
|
|
|
|
|
|
#define CSR_STINT ((S)->aCSR[7] & 0x0800) /**< Software Timer Interrupt */
|
|
|
|
|
#define CSR_STINTE ((S)->aCSR[7] & 0x0400) /**< Software Timer Interrupt Enable */
|
|
|
|
|
#define CSR_STINT !!((S)->aCSR[7] & 0x0800) /**< Software Timer Interrupt */
|
|
|
|
|
#define CSR_STINTE !!((S)->aCSR[7] & 0x0400) /**< Software Timer Interrupt Enable */
|
|
|
|
|
|
|
|
|
|
#define CSR_DRX(S) ((S)->aCSR[15] & 0x0001) /**< Disable Receiver */
|
|
|
|
|
#define CSR_DTX(S) ((S)->aCSR[15] & 0x0002) /**< Disable Transmit */
|
|
|
|
|
#define CSR_LOOP(S) ((S)->aCSR[15] & 0x0004) /**< Loopback Enable */
|
|
|
|
|
#define CSR_DRCVPA(S) ((S)->aCSR[15] & 0x2000) /**< Disable Receive Physical Address */
|
|
|
|
|
#define CSR_DRCVBC(S) ((S)->aCSR[15] & 0x4000) /**< Disable Receive Broadcast */
|
|
|
|
|
#define CSR_PROM(S) ((S)->aCSR[15] & 0x8000) /**< Promiscuous Mode */
|
|
|
|
|
#define CSR_DRX(S) !!((S)->aCSR[15] & 0x0001) /**< Disable Receiver */
|
|
|
|
|
#define CSR_DTX(S) !!((S)->aCSR[15] & 0x0002) /**< Disable Transmit */
|
|
|
|
|
#define CSR_LOOP(S) !!((S)->aCSR[15] & 0x0004) /**< Loopback Enable */
|
|
|
|
|
#define CSR_DRCVPA(S) !!((S)->aCSR[15] & 0x2000) /**< Disable Receive Physical Address */
|
|
|
|
|
#define CSR_DRCVBC(S) !!((S)->aCSR[15] & 0x4000) /**< Disable Receive Broadcast */
|
|
|
|
|
#define CSR_PROM(S) !!((S)->aCSR[15] & 0x8000) /**< Promiscuous Mode */
|
|
|
|
|
|
|
|
|
|
/** @name CSR register accessors.
|
|
|
|
|
* @{ */
|
|
|
|
@@ -193,7 +192,7 @@ typedef struct {
|
|
|
|
|
mem_mapping_t mmio_mapping;
|
|
|
|
|
const char *name;
|
|
|
|
|
int board;
|
|
|
|
|
int is_pci, is_vlb;
|
|
|
|
|
int is_pci, is_vlb, is_isa;
|
|
|
|
|
int PCIBase;
|
|
|
|
|
int MMIOBase;
|
|
|
|
|
uint32_t base_address;
|
|
|
|
@@ -214,6 +213,7 @@ typedef struct {
|
|
|
|
|
uint8_t aPROM[16];
|
|
|
|
|
uint16_t aCSR[CSR_MAX_REG];
|
|
|
|
|
uint16_t aBCR[BCR_MAX_RAP];
|
|
|
|
|
uint16_t aMII[MII_MAX_REG];
|
|
|
|
|
/** The loopback transmit buffer (avoid stack allocations). */
|
|
|
|
|
uint8_t abLoopBuf[4096];
|
|
|
|
|
/** The recv buffer. */
|
|
|
|
@@ -222,7 +222,7 @@ typedef struct {
|
|
|
|
|
int iLog2DescSize;
|
|
|
|
|
/** Bits 16..23 in 16-bit mode */
|
|
|
|
|
uint32_t GCUpperPhys;
|
|
|
|
|
/** We are waitign/about to start waiting for more receive buffers. */
|
|
|
|
|
/** We are waiting/about to start waiting for more receive buffers. */
|
|
|
|
|
int fMaybeOutOfSpace;
|
|
|
|
|
/** True if we signal the guest that RX packets are missing. */
|
|
|
|
|
int fSignalRxMiss;
|
|
|
|
@@ -230,8 +230,10 @@ typedef struct {
|
|
|
|
|
uint32_t u32LinkSpeed;
|
|
|
|
|
/** Error counter for bad receive descriptors. */
|
|
|
|
|
uint32_t uCntBadRMD;
|
|
|
|
|
uint16_t u16CSR0LastSeenByGuest;
|
|
|
|
|
uint64_t last_poll;
|
|
|
|
|
uint8_t maclocal[6]; /* configured MAC (local) address */
|
|
|
|
|
pc_timer_t poll_timer;
|
|
|
|
|
pc_timer_t poll_timer, timer_soft_int;
|
|
|
|
|
} nic_t;
|
|
|
|
|
|
|
|
|
|
/** @todo All structs: big endian? */
|
|
|
|
@@ -402,7 +404,6 @@ pcnet_do_irq(nic_t *dev, int issue)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load transmit message descriptor
|
|
|
|
|
* Make sure we read the own flag first.
|
|
|
|
@@ -821,13 +822,24 @@ pcnetSoftReset(nic_t *dev)
|
|
|
|
|
CSR_RCVRL(dev) = 1;
|
|
|
|
|
CSR_XMTRL(dev) = 1;
|
|
|
|
|
dev->aCSR[80] = 0x1410;
|
|
|
|
|
if (dev->is_pci) {
|
|
|
|
|
|
|
|
|
|
switch (dev->board) {
|
|
|
|
|
case DEV_AM79C970A:
|
|
|
|
|
dev->aCSR[88] = 0x1003;
|
|
|
|
|
dev->aCSR[89] = 0x0262;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
case DEV_AM79C973:
|
|
|
|
|
dev->aCSR[88] = 0x5003;
|
|
|
|
|
dev->aCSR[89] = 0x0262;
|
|
|
|
|
break;
|
|
|
|
|
case DEV_AM79C960:
|
|
|
|
|
case DEV_AM79C960_EB:
|
|
|
|
|
case DEV_AM79C960_VLB:
|
|
|
|
|
dev->aCSR[88] = 0x3003;
|
|
|
|
|
dev->aCSR[89] = 0x0000;
|
|
|
|
|
dev->aCSR[89] = 0x0262;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dev->aCSR[94] = 0x0000;
|
|
|
|
|
dev->aCSR[100] = 0x0200;
|
|
|
|
|
dev->aCSR[103] = 0x0105;
|
|
|
|
@@ -841,45 +853,48 @@ pcnetSoftReset(nic_t *dev)
|
|
|
|
|
static void
|
|
|
|
|
pcnetUpdateIrq(nic_t *dev)
|
|
|
|
|
{
|
|
|
|
|
dev->iISR = 0;
|
|
|
|
|
int iISR = 0;
|
|
|
|
|
uint16_t csr0;
|
|
|
|
|
|
|
|
|
|
dev->aCSR[0] &= ~0x0080; /* clear INTR */
|
|
|
|
|
csr0 = dev->aCSR[0];
|
|
|
|
|
|
|
|
|
|
if ((!(dev->aCSR[3] & 0x4000) && (dev->aCSR[0] & 0x4000)) /* BABL */ ||
|
|
|
|
|
(!(dev->aCSR[3] & 0x1000) && (dev->aCSR[0] & 0x1000)) /* MISS */ ||
|
|
|
|
|
(!(dev->aCSR[3] & 0x0100) && (dev->aCSR[0] & 0x0100)) /* IDON */ ||
|
|
|
|
|
(!(dev->aCSR[3] & 0x0200) && (dev->aCSR[0] & 0x0200)) /* TINT */ ||
|
|
|
|
|
(!(dev->aCSR[3] & 0x0400) && (dev->aCSR[0] & 0x0400)) /* RINT */ ||
|
|
|
|
|
(!(dev->aCSR[3] & 0x0800) && (dev->aCSR[0] & 0x0800)) /* MERR */ ||
|
|
|
|
|
(!(dev->aCSR[4] & 0x0001) && (dev->aCSR[4] & 0x0002)) /* JAB */ ||
|
|
|
|
|
(!(dev->aCSR[4] & 0x0004) && (dev->aCSR[4] & 0x0008)) /* TXSTRT */ ||
|
|
|
|
|
(!(dev->aCSR[4] & 0x0010) && (dev->aCSR[4] & 0x0020)) /* RCVO */ ||
|
|
|
|
|
(!(dev->aCSR[4] & 0x0100) && (dev->aCSR[4] & 0x0200)) /* MFCO */ ||
|
|
|
|
|
((dev->aCSR[5] & 0x0040) && (dev->aCSR[5] & 0x0080)) /* EXDINT */ ||
|
|
|
|
|
((dev->aCSR[5] & 0x0008) && (dev->aCSR[5] & 0x0010)) /* MPINT */) {
|
|
|
|
|
dev->iISR = CSR_INEA(dev); /* CSR_INEA */
|
|
|
|
|
dev->aCSR[0] |= 0x0080; /* set INTR */
|
|
|
|
|
csr0 &= ~0x0080; /* clear INTR */
|
|
|
|
|
|
|
|
|
|
if (((csr0 & ~dev->aCSR[3]) & 0x5f00) ||
|
|
|
|
|
(((dev->aCSR[4]>>1) & ~dev->aCSR[4]) & 0x0115) ||
|
|
|
|
|
(((dev->aCSR[5]>>1) & dev->aCSR[5]) & 0x0048)) {
|
|
|
|
|
iISR = !!(csr0 & 0x0040); /* CSR_INEA */
|
|
|
|
|
csr0 |= 0x0080; /* set INTR */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dev->aCSR[4] & 0x0080) { /* UINT */
|
|
|
|
|
dev->aCSR[4] &= ~0x0080;
|
|
|
|
|
dev->aCSR[4] |= 0x0040;
|
|
|
|
|
dev->iISR = 1; /* CSR_INEA */
|
|
|
|
|
dev->aCSR[0] |= 0x0080; /* set INTR */
|
|
|
|
|
pcnetlog(3, "%s: user int\n", dev->name);
|
|
|
|
|
if (dev->aCSR[4] & 0x0080) { /* UINTCMD */
|
|
|
|
|
dev->aCSR[4] &= ~0x0080; /* clear UINTCMD */
|
|
|
|
|
dev->aCSR[4] |= 0x0040; /* set UINT */
|
|
|
|
|
pcnetlog(2, "%s: user int\n", dev->name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (((dev->aCSR[5] & 0x0400) && (dev->aCSR[5] & 0x0800)) /* SINT */ ||
|
|
|
|
|
((dev->aCSR[5] & 0x0100) && (dev->aCSR[5] & 0x0200)) /* SLPINT */ ) {
|
|
|
|
|
dev->iISR = 1; /* CSR_INEA */
|
|
|
|
|
dev->aCSR[0] |= 0x0080; /* set INTR */
|
|
|
|
|
if (dev->aCSR[4] & csr0 & 0x0040 /* CSR_INEA */) {
|
|
|
|
|
csr0 |= 0x0080; /* set INTR */
|
|
|
|
|
iISR = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pcnetlog(3, "%s: pcnetUpdateIrq: set irq isr=%02x, TINT=%04x\n", dev->name, !!dev->iISR, dev->aCSR[0] & 0x0200);
|
|
|
|
|
if (((dev->aCSR[5]>>1) & dev->aCSR[5]) & 0x0500) {
|
|
|
|
|
iISR = 1;
|
|
|
|
|
csr0 |= 0x0080; /* set INTR */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((dev->aCSR[7] & 0x0c00) == 0x0c00) /* STINT + STINTE */
|
|
|
|
|
iISR = 1;
|
|
|
|
|
|
|
|
|
|
dev->aCSR[0] = csr0;
|
|
|
|
|
|
|
|
|
|
pcnetlog(2, "%s: pcnetUpdateIrq: iISR=%d\n", dev->name, iISR);
|
|
|
|
|
|
|
|
|
|
/* normal path is to _not_ change the IRQ status */
|
|
|
|
|
if (dev->iISR)
|
|
|
|
|
pcnet_do_irq(dev, 1);
|
|
|
|
|
if (iISR != dev->iISR) {
|
|
|
|
|
pcnet_do_irq(dev, iISR);
|
|
|
|
|
dev->iISR = iISR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -894,35 +909,17 @@ pcnetInit(nic_t *dev)
|
|
|
|
|
#define PCNET_INIT() do { \
|
|
|
|
|
DMAPageRead(PHYSADDR(dev, CSR_IADR(dev)), \
|
|
|
|
|
(uint8_t *)&initblk, sizeof(initblk)); \
|
|
|
|
|
dev->aCSR[15] = le32_to_cpu(initblk.mode); \
|
|
|
|
|
dev->aCSR[15] = le16_to_cpu(initblk.mode); \
|
|
|
|
|
CSR_RCVRL(dev) = (initblk.rlen < 9) ? (1 << initblk.rlen) : 512; \
|
|
|
|
|
CSR_XMTRL(dev) = (initblk.tlen < 9) ? (1 << initblk.tlen) : 512; \
|
|
|
|
|
dev->aCSR[ 6] = (initblk.tlen << 12) | (initblk.rlen << 8); \
|
|
|
|
|
dev->aCSR[ 8] = le32_to_cpu(initblk.ladrf1); \
|
|
|
|
|
dev->aCSR[ 9] = le32_to_cpu(initblk.ladrf2); \
|
|
|
|
|
dev->aCSR[10] = le32_to_cpu(initblk.ladrf3); \
|
|
|
|
|
dev->aCSR[11] = le32_to_cpu(initblk.ladrf4); \
|
|
|
|
|
dev->aCSR[12] = le32_to_cpu(initblk.padr1); \
|
|
|
|
|
dev->aCSR[13] = le32_to_cpu(initblk.padr2); \
|
|
|
|
|
dev->aCSR[14] = le32_to_cpu(initblk.padr3); \
|
|
|
|
|
dev->GCRDRA = PHYSADDR(dev, initblk.rdra); \
|
|
|
|
|
dev->GCTDRA = PHYSADDR(dev, initblk.tdra); \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#define PCNET_INIT16() do { \
|
|
|
|
|
DMAPageRead(PHYSADDR(dev, CSR_IADR(dev)), \
|
|
|
|
|
(uint8_t *)&initblk, sizeof(initblk)); \
|
|
|
|
|
dev->aCSR[15] = le32_to_cpu(initblk.mode); \
|
|
|
|
|
CSR_RCVRL(dev) = (1 << initblk.rlen); \
|
|
|
|
|
CSR_XMTRL(dev) = (1 << initblk.tlen); \
|
|
|
|
|
dev->aCSR[ 6] = (initblk.tlen << 12) | (initblk.rlen << 8); \
|
|
|
|
|
dev->aCSR[ 8] = le32_to_cpu(initblk.ladrf1); \
|
|
|
|
|
dev->aCSR[ 9] = le32_to_cpu(initblk.ladrf2); \
|
|
|
|
|
dev->aCSR[10] = le32_to_cpu(initblk.ladrf3); \
|
|
|
|
|
dev->aCSR[11] = le32_to_cpu(initblk.ladrf4); \
|
|
|
|
|
dev->aCSR[12] = le32_to_cpu(initblk.padr1); \
|
|
|
|
|
dev->aCSR[13] = le32_to_cpu(initblk.padr2); \
|
|
|
|
|
dev->aCSR[14] = le32_to_cpu(initblk.padr3); \
|
|
|
|
|
dev->aCSR[ 8] = le16_to_cpu(initblk.ladrf1); \
|
|
|
|
|
dev->aCSR[ 9] = le16_to_cpu(initblk.ladrf2); \
|
|
|
|
|
dev->aCSR[10] = le16_to_cpu(initblk.ladrf3); \
|
|
|
|
|
dev->aCSR[11] = le16_to_cpu(initblk.ladrf4); \
|
|
|
|
|
dev->aCSR[12] = le16_to_cpu(initblk.padr1); \
|
|
|
|
|
dev->aCSR[13] = le16_to_cpu(initblk.padr2); \
|
|
|
|
|
dev->aCSR[14] = le16_to_cpu(initblk.padr3); \
|
|
|
|
|
dev->GCRDRA = PHYSADDR(dev, initblk.rdra); \
|
|
|
|
|
dev->GCTDRA = PHYSADDR(dev, initblk.tdra); \
|
|
|
|
|
} while (0)
|
|
|
|
@@ -936,14 +933,14 @@ pcnetInit(nic_t *dev)
|
|
|
|
|
} else {
|
|
|
|
|
struct INITBLK16 initblk;
|
|
|
|
|
dev->GCUpperPhys = (0xff00 & (uint32_t)dev->aCSR[2]) << 16;
|
|
|
|
|
PCNET_INIT16();
|
|
|
|
|
PCNET_INIT();
|
|
|
|
|
pcnetlog(3, "%s: initblk.rlen=%#04x, initblk.tlen=%#04x\n",
|
|
|
|
|
dev->name, initblk.rlen, initblk.tlen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef PCNET_INIT
|
|
|
|
|
|
|
|
|
|
int cbRxBuffers = 0;
|
|
|
|
|
size_t cbRxBuffers = 0;
|
|
|
|
|
for (i = CSR_RCVRL(dev); i >= 1; i--) {
|
|
|
|
|
RMD rmd;
|
|
|
|
|
uint32_t rdaddr = PHYSADDR(dev, pcnetRdraAddr(dev, i));
|
|
|
|
@@ -970,7 +967,7 @@ pcnetInit(nic_t *dev)
|
|
|
|
|
* usually 1536 bytes and should therefore not run into condition. If they are still
|
|
|
|
|
* short in RX buffers we notify this condition.
|
|
|
|
|
*/
|
|
|
|
|
dev->fSignalRxMiss = (cbRxBuffers == 0 || cbRxBuffers >= 32*128);
|
|
|
|
|
dev->fSignalRxMiss = (cbRxBuffers == 0 || cbRxBuffers >= 32*1024);
|
|
|
|
|
|
|
|
|
|
CSR_RCVRC(dev) = CSR_RCVRL(dev);
|
|
|
|
|
CSR_XMTRC(dev) = CSR_XMTRL(dev);
|
|
|
|
@@ -1042,7 +1039,6 @@ pcnetWakeupReceive(nic_t *dev)
|
|
|
|
|
* Note: Once a descriptor belongs to the network card (this driver), it cannot be changed
|
|
|
|
|
* by the host (the guest driver) anymore. Well, it could but the results are undefined by
|
|
|
|
|
* definition.
|
|
|
|
|
* @param fSkipCurrent if true, don't scan the current RDTE.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
pcnetRdtePoll(nic_t *dev)
|
|
|
|
@@ -1071,8 +1067,6 @@ pcnetRdtePoll(nic_t *dev)
|
|
|
|
|
CSR_CRBA(dev) = rmd.rmd0.rbadr; /* Receive Buffer Address */
|
|
|
|
|
CSR_CRBC(dev) = rmd.rmd1.bcnt; /* Receive Byte Count */
|
|
|
|
|
CSR_CRST(dev) = ((uint32_t *)&rmd)[1] >> 16; /* Receive Status */
|
|
|
|
|
if (dev->fMaybeOutOfSpace)
|
|
|
|
|
pcnetWakeupReceive(dev);
|
|
|
|
|
} else {
|
|
|
|
|
/* This is not problematic since we don't own the descriptor
|
|
|
|
|
* We actually do own it, otherwise pcnetRmdLoad would have returned false.
|
|
|
|
@@ -1218,7 +1212,6 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
|
|
|
|
int cbPacket;
|
|
|
|
|
uint8_t buf1[60];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (CSR_DRX(dev) || CSR_STOP(dev) || CSR_SPND(dev) || !size)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
@@ -1249,7 +1242,7 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
|
|
|
|
if (HOST_IS_OWNER(CSR_CRST(dev))) {
|
|
|
|
|
/* Not owned by controller. This should not be possible as
|
|
|
|
|
* we already called pcnetCanReceive(). */
|
|
|
|
|
const uint32_t cb = 1 << dev->iLog2DescSize;
|
|
|
|
|
const unsigned cb = 1 << dev->iLog2DescSize;
|
|
|
|
|
uint32_t GCPhys = dev->GCRDRA;
|
|
|
|
|
iRxDesc = CSR_RCVRL(dev);
|
|
|
|
|
|
|
|
|
@@ -1264,7 +1257,7 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
|
|
|
|
} else {
|
|
|
|
|
RTNETETHERHDR *pEth = (RTNETETHERHDR *)buf;
|
|
|
|
|
int fStrip = 0;
|
|
|
|
|
int len_802_3;
|
|
|
|
|
size_t len_802_3;
|
|
|
|
|
uint8_t *src = &dev->abRecvBuf[8];
|
|
|
|
|
uint32_t crda = CSR_CRDA(dev);
|
|
|
|
|
uint32_t next_crda;
|
|
|
|
@@ -1291,6 +1284,10 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
|
|
|
|
/* In loopback mode, Runt Packed Accept is always enabled internally;
|
|
|
|
|
* don't do any padding because guest may be looping back very short packets.
|
|
|
|
|
*/
|
|
|
|
|
if (!CSR_LOOP(dev))
|
|
|
|
|
while (size < 60)
|
|
|
|
|
src[size++] = 0;
|
|
|
|
|
|
|
|
|
|
uint32_t fcs = UINT32_MAX;
|
|
|
|
|
uint8_t *p = src;
|
|
|
|
|
|
|
|
|
@@ -1302,13 +1299,13 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
|
|
|
|
size += 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cbPacket = size;
|
|
|
|
|
cbPacket = (int)size;
|
|
|
|
|
|
|
|
|
|
pcnetRmdLoad(dev, &rmd, PHYSADDR(dev, crda), 0);
|
|
|
|
|
/* if (!CSR_LAPPEN(dev)) */
|
|
|
|
|
rmd.rmd1.stp = 1;
|
|
|
|
|
|
|
|
|
|
int cbBuf = MIN(4096 - rmd.rmd1.bcnt, size);
|
|
|
|
|
size_t cbBuf = MIN(4096 - rmd.rmd1.bcnt, size);
|
|
|
|
|
uint32_t rbadr = PHYSADDR(dev, rmd.rmd0.rbadr);
|
|
|
|
|
|
|
|
|
|
/* save the old value to check if it was changed as long as we didn't
|
|
|
|
@@ -1362,7 +1359,7 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
|
|
|
|
crda = next_crda;
|
|
|
|
|
rmd = next_rmd;
|
|
|
|
|
|
|
|
|
|
cbBuf = MIN(4096 - rmd.rmd1.bcnt, size);
|
|
|
|
|
cbBuf = MIN(4096 - (size_t)rmd.rmd1.bcnt, size);
|
|
|
|
|
uint32_t rbadr2 = PHYSADDR(dev, rmd.rmd0.rbadr);
|
|
|
|
|
|
|
|
|
|
/* We have to leave the critical section here or we risk deadlocking
|
|
|
|
@@ -1398,7 +1395,7 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
|
|
|
|
rmd.rmd2.mcnt = cbPacket;
|
|
|
|
|
rmd.rmd2.zeros = 0;
|
|
|
|
|
} else {
|
|
|
|
|
pcnetlog(2, "%s: Overflow by %ubytes\n", dev->name, size);
|
|
|
|
|
pcnetlog(1, "%s: Overflow by %ubytes\n", dev->name, size);
|
|
|
|
|
rmd.rmd1.oflo = 1;
|
|
|
|
|
rmd.rmd1.buff = 1;
|
|
|
|
|
rmd.rmd1.err = 1;
|
|
|
|
@@ -1408,7 +1405,7 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
|
|
|
|
pcnetRmdStorePassHost(dev, &rmd, PHYSADDR(dev, crda));
|
|
|
|
|
|
|
|
|
|
dev->aCSR[0] |= 0x0400;
|
|
|
|
|
pcnetlog(2, "%s: RINT set, RCVRC=%d CRDA=%#010x\n", dev->name,
|
|
|
|
|
pcnetlog(1, "%s: RINT set, RCVRC=%d CRDA=%#010x\n", dev->name,
|
|
|
|
|
CSR_RCVRC(dev), PHYSADDR(dev, CSR_CRDA(dev)));
|
|
|
|
|
|
|
|
|
|
/* guest driver is owner: force repoll of current and next RDTEs */
|
|
|
|
@@ -1416,9 +1413,6 @@ pcnetReceiveNoSync(void *priv, uint8_t *buf, int size)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!CSR_LOOP(dev))
|
|
|
|
|
pcnetPollRxTx(dev);
|
|
|
|
|
|
|
|
|
|
pcnetUpdateIrq(dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1442,6 +1436,7 @@ pcnetAsyncTransmit(nic_t *dev)
|
|
|
|
|
/*
|
|
|
|
|
* Iterate the transmit descriptors.
|
|
|
|
|
*/
|
|
|
|
|
unsigned cFlushIrq = 0;
|
|
|
|
|
int cMax = 32;
|
|
|
|
|
do {
|
|
|
|
|
TMD tmd;
|
|
|
|
@@ -1457,32 +1452,39 @@ pcnetAsyncTransmit(nic_t *dev)
|
|
|
|
|
*/
|
|
|
|
|
if (tmd.tmd1.stp && tmd.tmd1.enp) {
|
|
|
|
|
const int cb = 4096 - tmd.tmd1.bcnt;
|
|
|
|
|
pcnetlog(1, "%s: pcnetAsyncTransmit: stp&enp: cb=%d xmtrc=%#x\n", dev->name, cb, CSR_XMTRC(dev));
|
|
|
|
|
dev->xmit_pos = cb;
|
|
|
|
|
DMAPageRead(PHYSADDR(dev, tmd.tmd0.tbadr), dev->abLoopBuf, cb);
|
|
|
|
|
pcnetlog("%s: pcnetAsyncTransmit: stp&enp: cb=%d xmtrc=%#x\n", dev->name, cb, CSR_XMTRC(dev));
|
|
|
|
|
|
|
|
|
|
if (fLoopback) {
|
|
|
|
|
/* From the manual: ``A zero length buffer is acceptable as
|
|
|
|
|
* long as it is not the last buffer in a chain (STP = 0 and
|
|
|
|
|
* ENP = 1).'' That means that the first buffer might have a
|
|
|
|
|
* zero length if it is not the last one in the chain. */
|
|
|
|
|
if (cb <= MAX_FRAME) {
|
|
|
|
|
dev->xmit_pos = cb;
|
|
|
|
|
DMAPageRead(PHYSADDR(dev, tmd.tmd0.tbadr), dev->abLoopBuf, cb);
|
|
|
|
|
|
|
|
|
|
if (fLoopback) {
|
|
|
|
|
if (HOST_IS_OWNER(CSR_CRST(dev)))
|
|
|
|
|
pcnetRdtePoll(dev);
|
|
|
|
|
|
|
|
|
|
pcnetReceiveNoSync(dev, dev->abLoopBuf, dev->xmit_pos);
|
|
|
|
|
} else {
|
|
|
|
|
pcnetlog(3, "%s: pcnetAsyncTransmit: transmit loopbuf stp and enp, xmit pos = %d\n", dev->name, dev->xmit_pos);
|
|
|
|
|
network_tx(dev->abLoopBuf, dev->xmit_pos);
|
|
|
|
|
}
|
|
|
|
|
} else if (cb == 4096) {
|
|
|
|
|
/* The Windows NT4 pcnet driver sometimes marks the first
|
|
|
|
|
* unused descriptor as owned by us. Ignore that (by
|
|
|
|
|
* passing it back). Do not update the ring counter in this
|
|
|
|
|
* case (otherwise that driver becomes even more confused,
|
|
|
|
|
* which causes transmit to stall for about 10 seconds).
|
|
|
|
|
* This is just a workaround, not a final solution. */
|
|
|
|
|
* This is just a workaround, not a final solution.
|
|
|
|
|
*/
|
|
|
|
|
/* r=frank: IMHO this is the correct implementation. The
|
|
|
|
|
* manual says: ``If the OWN bit is set and the buffer
|
|
|
|
|
* length is 0, the OWN bit will be cleared. In the C-LANCE
|
|
|
|
|
* the buffer length of 0 is interpreted as a 4096-byte
|
|
|
|
|
* buffer.'' */
|
|
|
|
|
* buffer.''
|
|
|
|
|
*/
|
|
|
|
|
/* r=michaln: Perhaps not quite right. The C-LANCE (Am79C90)
|
|
|
|
|
* datasheet explains that the old LANCE (Am7990) ignored
|
|
|
|
|
* the top four bits next to BCNT and a count of 0 was
|
|
|
|
@@ -1499,10 +1501,6 @@ pcnetAsyncTransmit(nic_t *dev)
|
|
|
|
|
pcnetTmdStorePassHost(dev, &tmd, PHYSADDR(dev, CSR_CXDA(dev)));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
pcnetlog(3, "%s: pcnetAsyncTransmit: transmit loopbuf stp and enp\n", dev->name);
|
|
|
|
|
network_tx(dev->abLoopBuf, dev->xmit_pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Write back the TMD and pass it to the host (clear own bit). */
|
|
|
|
|
pcnetTmdStorePassHost(dev, &tmd, PHYSADDR(dev, CSR_CXDA(dev)));
|
|
|
|
@@ -1521,18 +1519,9 @@ pcnetAsyncTransmit(nic_t *dev)
|
|
|
|
|
* waste time finding out how much space we actually need even if
|
|
|
|
|
* we could reliably do that on SMP guests.
|
|
|
|
|
*/
|
|
|
|
|
int cb = 4096 - tmd.tmd1.bcnt;
|
|
|
|
|
unsigned cb = 4096 - tmd.tmd1.bcnt;
|
|
|
|
|
dev->xmit_pos = pcnetCalcPacketLen(dev, cb);
|
|
|
|
|
DMAPageRead(PHYSADDR(dev, tmd.tmd0.tbadr), dev->abLoopBuf, cb);
|
|
|
|
|
if (fLoopback) {
|
|
|
|
|
if (HOST_IS_OWNER(CSR_CRST(dev)))
|
|
|
|
|
pcnetRdtePoll(dev);
|
|
|
|
|
pcnetlog(3, "%s: pcnetAsyncTransmit: receive loopback stp\n", dev->name);
|
|
|
|
|
pcnetReceiveNoSync(dev, dev->abLoopBuf, dev->xmit_pos);
|
|
|
|
|
} else {
|
|
|
|
|
pcnetlog(3, "%s: pcnetAsyncTransmit: transmit loopbuf stp\n", dev->name);
|
|
|
|
|
network_tx(dev->abLoopBuf, dev->xmit_pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
/*
|
|
|
|
@@ -1551,6 +1540,8 @@ pcnetAsyncTransmit(nic_t *dev)
|
|
|
|
|
*/
|
|
|
|
|
tmd.tmd2.buff = tmd.tmd2.uflo = tmd.tmd1.err = 1;
|
|
|
|
|
dev->aCSR[0] |= 0x0200; /* set TINT */
|
|
|
|
|
/* Don't allow the guest to clear TINT before reading it */
|
|
|
|
|
dev->u16CSR0LastSeenByGuest &= ~0x0200;
|
|
|
|
|
if (!CSR_DXSUFLO(dev)) /* stop on xmit underflow */
|
|
|
|
|
dev->aCSR[0] &= ~0x0010; /* clear TXON */
|
|
|
|
|
pcnetTmdStorePassHost(dev, &tmd, GCPhysPrevTmd);
|
|
|
|
@@ -1577,6 +1568,9 @@ pcnetAsyncTransmit(nic_t *dev)
|
|
|
|
|
*/
|
|
|
|
|
if (tmd.tmd1.enp) {
|
|
|
|
|
if (fLoopback) {
|
|
|
|
|
if (HOST_IS_OWNER(CSR_CRST(dev)))
|
|
|
|
|
pcnetRdtePoll(dev);
|
|
|
|
|
|
|
|
|
|
pcnetlog(3, "%s: pcnetAsyncTransmit: receive loopback enp\n", dev->name);
|
|
|
|
|
pcnetReceiveNoSync(dev, dev->abLoopBuf, dev->xmit_pos);
|
|
|
|
|
} else {
|
|
|
|
@@ -1599,15 +1593,22 @@ pcnetAsyncTransmit(nic_t *dev)
|
|
|
|
|
/* Update TDMD, TXSTRT and TINT. */
|
|
|
|
|
dev->aCSR[0] &= ~0x0008; /* clear TDMD */
|
|
|
|
|
dev->aCSR[4] |= 0x0008; /* set TXSTRT */
|
|
|
|
|
if ( !CSR_TOKINTD(dev) /* Transmit OK Interrupt Disable, no infl. on errors. */
|
|
|
|
|
dev->xmit_pos = -1;
|
|
|
|
|
if (!CSR_TOKINTD(dev) /* Transmit OK Interrupt Disable, no infl. on errors. */
|
|
|
|
|
|| (CSR_LTINTEN(dev) && tmd.tmd1.ltint)
|
|
|
|
|
|| tmd.tmd1.err) {
|
|
|
|
|
dev->aCSR[0] |= 0x0200; /* set TINT */
|
|
|
|
|
pcnetUpdateIrq(dev);
|
|
|
|
|
cFlushIrq++;
|
|
|
|
|
}
|
|
|
|
|
if (--cMax == 0)
|
|
|
|
|
break;
|
|
|
|
|
} while (CSR_TXON(dev)); /* transfer on */
|
|
|
|
|
|
|
|
|
|
if (cFlushIrq) {
|
|
|
|
|
dev->aCSR[0] |= 0x0200; /* set TINT */
|
|
|
|
|
/* Don't allow the guest to clear TINT before reading it */
|
|
|
|
|
dev->u16CSR0LastSeenByGuest &= ~0x0200;
|
|
|
|
|
pcnetUpdateIrq(dev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1650,24 +1651,34 @@ pcnetHardReset(nic_t *dev)
|
|
|
|
|
{
|
|
|
|
|
pcnetlog(2, "%s: pcnetHardReset\n", dev->name);
|
|
|
|
|
|
|
|
|
|
dev->iISR = 0;
|
|
|
|
|
pcnet_do_irq(dev, 0);
|
|
|
|
|
|
|
|
|
|
/* Many of the BCR values would normally be read from the EEPROM. */
|
|
|
|
|
dev->aBCR[BCR_MSRDA] = 0x0005;
|
|
|
|
|
dev->aBCR[BCR_MSWRA] = 0x0005;
|
|
|
|
|
dev->aBCR[BCR_MC ] = 0x0002;
|
|
|
|
|
dev->aBCR[BCR_MC] = 0x0002;
|
|
|
|
|
dev->aBCR[BCR_LNKST] = 0x00c0;
|
|
|
|
|
dev->aBCR[BCR_LED1 ] = 0x0084;
|
|
|
|
|
dev->aBCR[BCR_LED2 ] = 0x0088;
|
|
|
|
|
dev->aBCR[BCR_LED3 ] = 0x0090;
|
|
|
|
|
dev->aBCR[BCR_LED1] = 0x0084;
|
|
|
|
|
dev->aBCR[BCR_LED2] = 0x0088;
|
|
|
|
|
dev->aBCR[BCR_LED3] = 0x0090;
|
|
|
|
|
|
|
|
|
|
/* For ISA PnP cards, BCR8 reports IRQ/DMA (e.g. 0x0035 means IRQ 3, DMA 5). */
|
|
|
|
|
if (dev->board == PCNET_ISA)
|
|
|
|
|
dev->aBCR[8] = (dev->dma_channel) | (dev->base_irq << 4);
|
|
|
|
|
if (dev->is_isa)
|
|
|
|
|
dev->aBCR[8] = dev->dma_channel | (dev->base_irq << 4);
|
|
|
|
|
|
|
|
|
|
dev->aBCR[BCR_FDC ] = 0x0000;
|
|
|
|
|
dev->aBCR[BCR_BSBC ] = 0x9001;
|
|
|
|
|
dev->aBCR[BCR_FDC] = 0x0000;
|
|
|
|
|
dev->aBCR[BCR_BSBC] = 0x9001;
|
|
|
|
|
dev->aBCR[BCR_EECAS] = 0x0002;
|
|
|
|
|
dev->aBCR[BCR_SWS ] = 0x0200;
|
|
|
|
|
dev->aBCR[BCR_STVAL] = 0xffff;
|
|
|
|
|
dev->aCSR[58] = dev->aBCR[BCR_SWS] = 0x0200; /* CSR58 is an alias for BCR20 */
|
|
|
|
|
dev->iLog2DescSize = 3;
|
|
|
|
|
dev->aBCR[BCR_PLAT ] = 0xff06;
|
|
|
|
|
dev->aBCR[BCR_PLAT] = 0xff06;
|
|
|
|
|
dev->aBCR[BCR_MIICAS] = 0x20; /* Auto-negotiation on. */
|
|
|
|
|
dev->aBCR[BCR_MIIADDR] = 0; /* Internal PHY on Am79C973 would be (0x1e << 5) */
|
|
|
|
|
dev->aBCR[BCR_PCIVID] = 0x1022;
|
|
|
|
|
dev->aBCR[BCR_PCISID] = 0x0020;
|
|
|
|
|
dev->aBCR[BCR_PCISVID] = 0x1022;
|
|
|
|
|
|
|
|
|
|
/* Reset the error counter. */
|
|
|
|
|
dev->uCntBadRMD = 0;
|
|
|
|
@@ -1679,15 +1690,16 @@ pcnetHardReset(nic_t *dev)
|
|
|
|
|
static void
|
|
|
|
|
pcnet_csr_writew(nic_t *dev, uint16_t rap, uint16_t val)
|
|
|
|
|
{
|
|
|
|
|
uint16_t old_inea = CSR_INEA(dev);
|
|
|
|
|
|
|
|
|
|
pcnetlog(1, "%s: pcnet_csr_writew: rap=%d val=%#06x\n", dev->name, rap, val);
|
|
|
|
|
switch (rap) {
|
|
|
|
|
case 0:
|
|
|
|
|
/* Clear any interrupt flags. */
|
|
|
|
|
dev->aCSR[0] &= ~(val & 0x7f00);
|
|
|
|
|
dev->aCSR[0] = (dev->aCSR[0] & ~0x0040) | (val & 0x0048);
|
|
|
|
|
val = (val & 0x007f) | (dev->aCSR[0] & 0x7f00);
|
|
|
|
|
{
|
|
|
|
|
uint16_t csr0 = dev->aCSR[0];
|
|
|
|
|
/* Clear any interrupt flags.
|
|
|
|
|
* Don't clear an interrupt flag which was not seen by the guest yet. */
|
|
|
|
|
csr0 &= ~(val & 0x7f00 & dev->u16CSR0LastSeenByGuest);
|
|
|
|
|
csr0 = (csr0 & ~0x0040) | (val & 0x0048);
|
|
|
|
|
val = (val & 0x007f) | (csr0 & 0x7f00);
|
|
|
|
|
|
|
|
|
|
/* If STOP, STRT and INIT are set, clear STRT and INIT */
|
|
|
|
|
if ((val & 7) == 7)
|
|
|
|
@@ -1695,12 +1707,7 @@ pcnet_csr_writew(nic_t *dev, uint16_t rap, uint16_t val)
|
|
|
|
|
|
|
|
|
|
pcnetlog(2, "%s: CSR0 val = %04x, val2 = %04x\n", dev->name, val, dev->aCSR[0]);
|
|
|
|
|
|
|
|
|
|
if (old_inea && !(val & 0x40)) {
|
|
|
|
|
pcnetlog(3, "%s: pcnet_csr_writew(): Disable IRQ\n", dev->name);
|
|
|
|
|
dev->iISR = 0;
|
|
|
|
|
dev->aCSR[0] &= ~0x0080;
|
|
|
|
|
pcnet_do_irq(dev, 0);
|
|
|
|
|
}
|
|
|
|
|
dev->aCSR[0] = csr0;
|
|
|
|
|
|
|
|
|
|
if (!CSR_STOP(dev) && (val & 4)) {
|
|
|
|
|
pcnetlog(3, "%s: pcnet_csr_writew(): Stop\n", dev->name);
|
|
|
|
@@ -1721,10 +1728,11 @@ pcnet_csr_writew(nic_t *dev, uint16_t rap, uint16_t val)
|
|
|
|
|
pcnetlog(3, "%s: pcnet_csr_writew(): Transmit\n", dev->name);
|
|
|
|
|
pcnetAsyncTransmit(dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case 2: /* IADRH */
|
|
|
|
|
if (dev->board == PCNET_ISA)
|
|
|
|
|
if (dev->is_isa)
|
|
|
|
|
val &= 0x00ff; /* Upper 8 bits ignored on ISA chips. */
|
|
|
|
|
case 1: /* IADRL */
|
|
|
|
|
case 8: /* LADRF 0..15 */
|
|
|
|
@@ -1778,10 +1786,21 @@ pcnet_csr_writew(nic_t *dev, uint16_t rap, uint16_t val)
|
|
|
|
|
val &= ~0x0a90;
|
|
|
|
|
val |= dev->aCSR[5] & 0x0a90;
|
|
|
|
|
break;
|
|
|
|
|
case 16:
|
|
|
|
|
case 7: /* Extended Control and Interrupt 2 */
|
|
|
|
|
{
|
|
|
|
|
uint16_t csr7 = dev->aCSR[7];
|
|
|
|
|
csr7 &= ~0x0400;
|
|
|
|
|
csr7 &= ~(val & 0x0800);
|
|
|
|
|
csr7 |= (val & 0x0400);
|
|
|
|
|
dev->aCSR[7] = csr7;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case 15: /* Mode */
|
|
|
|
|
break;
|
|
|
|
|
case 16: /* IADRL */
|
|
|
|
|
pcnet_csr_writew(dev,1,val);
|
|
|
|
|
return;
|
|
|
|
|
case 17:
|
|
|
|
|
case 17: /* IADRH */
|
|
|
|
|
pcnet_csr_writew(dev,2,val);
|
|
|
|
|
return;
|
|
|
|
|
/*
|
|
|
|
@@ -1816,7 +1835,9 @@ pcnet_csr_writew(nic_t *dev, uint16_t rap, uint16_t val)
|
|
|
|
|
dev->GCTDRA = (dev->GCTDRA & 0xffff0000) | (val & 0x0000ffff);
|
|
|
|
|
else
|
|
|
|
|
dev->GCTDRA = (dev->GCTDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
|
|
|
|
|
|
|
|
|
|
pcnetlog(3, "%s: WRITE CSR%d, %#06x => GCTDRA=%08x (alt init)\n", dev->name, rap, val, dev->GCTDRA);
|
|
|
|
|
|
|
|
|
|
if (dev->GCTDRA & (dev->iLog2DescSize - 1))
|
|
|
|
|
pcnetlog(1, "%s: Warning: Misaligned TDRA (GCTDRA=%#010x)\n", dev->name, dev->GCTDRA);
|
|
|
|
|
break;
|
|
|
|
@@ -1878,6 +1899,7 @@ pcnet_csr_readw(nic_t *dev, uint16_t rap)
|
|
|
|
|
pcnetUpdateIrq(dev);
|
|
|
|
|
val = dev->aCSR[0];
|
|
|
|
|
val |= (val & 0x7800) ? 0x8000 : 0;
|
|
|
|
|
dev->u16CSR0LastSeenByGuest = val;
|
|
|
|
|
break;
|
|
|
|
|
case 16:
|
|
|
|
|
return pcnet_csr_readw(dev,1);
|
|
|
|
@@ -1930,6 +1952,7 @@ pcnet_bcr_writew(nic_t *dev, uint16_t rap, uint16_t val)
|
|
|
|
|
dev->GCUpperPhys = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
dev->aCSR[58] = val;
|
|
|
|
|
/* fall through */
|
|
|
|
|
case BCR_LNKST:
|
|
|
|
|
case BCR_LED1:
|
|
|
|
@@ -1940,13 +1963,156 @@ pcnet_bcr_writew(nic_t *dev, uint16_t rap, uint16_t val)
|
|
|
|
|
case BCR_BSBC:
|
|
|
|
|
case BCR_EECAS:
|
|
|
|
|
case BCR_PLAT:
|
|
|
|
|
case BCR_MIICAS:
|
|
|
|
|
case BCR_MIIADDR:
|
|
|
|
|
dev->aBCR[rap] = val;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BCR_STVAL:
|
|
|
|
|
val &= 0xffff;
|
|
|
|
|
dev->aBCR[BCR_STVAL] = val;
|
|
|
|
|
if (dev->board == DEV_AM79C973)
|
|
|
|
|
timer_set_delay_u64(&dev->timer_soft_int, (12.8 * val) * TIMER_USEC);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BCR_MIIMDR:
|
|
|
|
|
dev->aMII[dev->aBCR[BCR_MIIADDR] & 0x1f] = val;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
|
pcnet_mii_readw(nic_t *dev, uint16_t miiaddr)
|
|
|
|
|
{
|
|
|
|
|
uint16_t val;
|
|
|
|
|
int autoneg, duplex, fast, isolate;
|
|
|
|
|
|
|
|
|
|
/* If the DANAS (BCR32.7) bit is set, the MAC does not do any
|
|
|
|
|
* auto-negotiation and the PHY must be set up explicitly. DANAS
|
|
|
|
|
* effectively disables most other BCR32 bits.
|
|
|
|
|
*/
|
|
|
|
|
if (dev->aBCR[BCR_MIICAS] & 0x80) {
|
|
|
|
|
/* PHY controls auto-negotiation. */
|
|
|
|
|
autoneg = duplex = fast = 1;
|
|
|
|
|
} else {
|
|
|
|
|
/* BCR32 controls auto-negotiation. */
|
|
|
|
|
autoneg = (dev->aBCR[BCR_MIICAS] & 0x20) != 0;
|
|
|
|
|
duplex = (dev->aBCR[BCR_MIICAS] & 0x10) != 0;
|
|
|
|
|
fast = (dev->aBCR[BCR_MIICAS] & 0x08) != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Electrically isolating the PHY mostly disables it. */
|
|
|
|
|
isolate = (dev->aMII[0] & 0x400) != 0;
|
|
|
|
|
|
|
|
|
|
switch (miiaddr) {
|
|
|
|
|
case 0:
|
|
|
|
|
/* MII basic mode control register. */
|
|
|
|
|
val = 0;
|
|
|
|
|
if (autoneg)
|
|
|
|
|
val |= 0x1000; /* Enable auto negotiation. */
|
|
|
|
|
if (fast)
|
|
|
|
|
val |= 0x2000; /* 100 Mbps */
|
|
|
|
|
if (duplex) /* Full duplex forced */
|
|
|
|
|
val |= 0x0100; /* Full duplex */
|
|
|
|
|
if (isolate) /* PHY electrically isolated. */
|
|
|
|
|
val |= 0x0400; /* Isolated */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
/* MII basic mode status register. */
|
|
|
|
|
val = 0x7800 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
|
|
|
|
|
| 0x0040 /* Mgmt frame preamble not required. */
|
|
|
|
|
| 0x0020 /* Auto-negotiation complete. */
|
|
|
|
|
| 0x0008 /* Able to do auto-negotiation. */
|
|
|
|
|
| 0x0004 /* Link up. */
|
|
|
|
|
| 0x0001; /* Extended Capability, i.e. registers 4+ valid. */
|
|
|
|
|
if (isolate) {
|
|
|
|
|
val &= ~(0x0020 | 0x0004);
|
|
|
|
|
}
|
|
|
|
|
if (!autoneg) {
|
|
|
|
|
/* Auto-negotiation disabled. */
|
|
|
|
|
if (duplex)
|
|
|
|
|
val &= ~0x2800; /* Full duplex forced. */
|
|
|
|
|
else
|
|
|
|
|
val &= ~0x5000; /* Half duplex forced. */
|
|
|
|
|
|
|
|
|
|
if (fast)
|
|
|
|
|
val &= ~0x1800; /* 100 Mbps forced */
|
|
|
|
|
else
|
|
|
|
|
val &= ~0x6000; /* 10 Mbps forced */
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
/* PHY identifier 1. */
|
|
|
|
|
val = 0x22; /* Am79C874/AC101 PHY */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
/* PHY identifier 2. */
|
|
|
|
|
val = 0x561b; /* Am79C874/AC101 PHY */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
|
/* Advertisement control register. */
|
|
|
|
|
val = 0x01e0 /* Try 100mbps FD/HD and 10mbps FD/HD. */
|
|
|
|
|
| 0x0001; /* CSMA selector. */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
|
/* Link partner ability register. */
|
|
|
|
|
if (!isolate) {
|
|
|
|
|
val = 0x8000 /* Next page bit. */
|
|
|
|
|
| 0x4000 /* Link partner acked us. */
|
|
|
|
|
| 0x0400 /* Can do flow control. */
|
|
|
|
|
| 0x01e0 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
|
|
|
|
|
| 0x0001; /* Use CSMA selector. */
|
|
|
|
|
} else {
|
|
|
|
|
val = 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
|
/* Auto negotiation expansion register. */
|
|
|
|
|
if (!isolate) {
|
|
|
|
|
val = 0x0008 /* Link partner supports npage. */
|
|
|
|
|
| 0x0004 /* Enable npage words. */
|
|
|
|
|
| 0x0001; /* Can do N-way auto-negotiation. */
|
|
|
|
|
} else {
|
|
|
|
|
val = 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 18:
|
|
|
|
|
/* Diagnostic Register (FreeBSD pcn/ac101 driver reads this). */
|
|
|
|
|
if (!isolate) {
|
|
|
|
|
val = 0x1000 /* Receive PLL locked. */
|
|
|
|
|
| 0x0200; /* Signal detected. */
|
|
|
|
|
|
|
|
|
|
if (autoneg) {
|
|
|
|
|
val |= 0x0400 /* 100Mbps rate. */
|
|
|
|
|
| 0x0800; /* Full duplex. */
|
|
|
|
|
} else {
|
|
|
|
|
if (fast)
|
|
|
|
|
val |= 0x0400; /* 100Mbps rate. */
|
|
|
|
|
if (duplex)
|
|
|
|
|
val |= 0x0800; /* Full duplex. */
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
val = 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
val = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint16_t
|
|
|
|
|
pcnet_bcr_readw(nic_t *dev, uint16_t rap)
|
|
|
|
@@ -1961,6 +2127,14 @@ pcnet_bcr_readw(nic_t *dev, uint16_t rap)
|
|
|
|
|
val = dev->aBCR[rap] & ~0x8000;
|
|
|
|
|
val |= (val & 0x017f & dev->u32Lnkst) ? 0x8000 : 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BCR_MIIADDR:
|
|
|
|
|
if ((dev->board == DEV_AM79C973) && (((dev->aBCR[BCR_MIIADDR] >> 5) & 0x1f) == 0)) {
|
|
|
|
|
uint16_t miiaddr = dev->aBCR[BCR_MIIADDR] & 0x1f;
|
|
|
|
|
val = pcnet_mii_readw(dev, miiaddr);
|
|
|
|
|
} else
|
|
|
|
|
val = 0xffff;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
val = rap < BCR_MAX_RAP ? dev->aBCR[rap] : 0;
|
|
|
|
|
break;
|
|
|
|
@@ -2409,7 +2583,7 @@ pcnet_pci_read(int func, int addr, void *p)
|
|
|
|
|
case 0x07:
|
|
|
|
|
return 2;
|
|
|
|
|
case 0x08:
|
|
|
|
|
return 0x10; /*Revision ID*/
|
|
|
|
|
return (dev->board == DEV_AM79C973) ? 0x40 : 0x10; /*Revision ID*/
|
|
|
|
|
case 0x09:
|
|
|
|
|
return 0; /*Programming interface*/
|
|
|
|
|
case 0x0A:
|
|
|
|
@@ -2436,6 +2610,14 @@ pcnet_pci_read(int func, int addr, void *p)
|
|
|
|
|
return pcnet_pci_bar[1].addr_regs[2];
|
|
|
|
|
case 0x17:
|
|
|
|
|
return pcnet_pci_bar[1].addr_regs[3];
|
|
|
|
|
case 0x2C:
|
|
|
|
|
return 0x22;
|
|
|
|
|
case 0x2D:
|
|
|
|
|
return 0x10;
|
|
|
|
|
case 0x2E:
|
|
|
|
|
return 0x00;
|
|
|
|
|
case 0x2F:
|
|
|
|
|
return 0x20;
|
|
|
|
|
case 0x3C:
|
|
|
|
|
return dev->base_irq;
|
|
|
|
|
case 0x3D:
|
|
|
|
@@ -2490,6 +2672,26 @@ pcnetWaitReceiveAvail(void *priv)
|
|
|
|
|
return dev->fMaybeOutOfSpace;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
pcnetTimerSoftInt(void *priv)
|
|
|
|
|
{
|
|
|
|
|
nic_t *dev = (nic_t *) priv;
|
|
|
|
|
|
|
|
|
|
dev->aCSR[7] |= 0x0800; /* STINT */
|
|
|
|
|
pcnetUpdateIrq(dev);
|
|
|
|
|
timer_advance_u64(&dev->timer_soft_int, (12.8 * (dev->aBCR[BCR_STVAL] & 0xffff)) * TIMER_USEC);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
pcnetTimerCallback(void *priv)
|
|
|
|
|
{
|
|
|
|
|
nic_t *dev = (nic_t *) priv;
|
|
|
|
|
|
|
|
|
|
pcnetlog("Timer Callback to RX\n");
|
|
|
|
|
pcnetPollRxTx(dev);
|
|
|
|
|
|
|
|
|
|
timer_disable(&dev->poll_timer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void *
|
|
|
|
|
pcnet_init(const device_t *info)
|
|
|
|
@@ -2515,6 +2717,7 @@ pcnet_init(const device_t *info)
|
|
|
|
|
|
|
|
|
|
dev->is_pci = !!(info->flags & DEVICE_PCI);
|
|
|
|
|
dev->is_vlb = !!(info->flags & DEVICE_VLB);
|
|
|
|
|
dev->is_isa = !!(info->flags & (DEVICE_ISA | DEVICE_AT));
|
|
|
|
|
|
|
|
|
|
if (dev->is_pci) {
|
|
|
|
|
pcnet_mem_init(dev, 0x0fffff00);
|
|
|
|
@@ -2553,6 +2756,7 @@ pcnet_init(const device_t *info)
|
|
|
|
|
dev->aPROM[8] = 0x00;
|
|
|
|
|
|
|
|
|
|
/* Hardware ID: must be 11h if compatibility to AMD drivers is desired */
|
|
|
|
|
/* 0x00/0xFF=ISA, 0x01=PnP, 0x10=VLB, 0x11=PCI */
|
|
|
|
|
if (dev->is_pci)
|
|
|
|
|
dev->aPROM[9] = 0x11;
|
|
|
|
|
else if (dev->is_vlb)
|
|
|
|
@@ -2563,13 +2767,14 @@ pcnet_init(const device_t *info)
|
|
|
|
|
/* User programmable space, init with 0 */
|
|
|
|
|
dev->aPROM[10] = dev->aPROM[11] = 0x00;
|
|
|
|
|
|
|
|
|
|
/* LSByte of two-byte checksum, which is the sum of bytes 00h-0Bh
|
|
|
|
|
and bytes 0Eh and 0Fh, must therefore be initialized with 0! */
|
|
|
|
|
dev->aPROM[12] = dev->aPROM[13] = 0x00;
|
|
|
|
|
|
|
|
|
|
if (dev->board == DEV_AM79C960_EB) {
|
|
|
|
|
dev->aPROM[14] = 0x52;
|
|
|
|
|
dev->aPROM[15] = 0x44; /* NI6510 EtherBlaster 'RD' signature. */
|
|
|
|
|
} else {
|
|
|
|
|
/* Must be ASCII W (57h) if compatibility to AMD
|
|
|
|
|
driver software is desired */
|
|
|
|
|
dev->aPROM[14] = dev->aPROM[15] = 0x57;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (c = 0, checksum = 0; c < 16; c++)
|
|
|
|
|
checksum += dev->aPROM[c];
|
|
|
|
@@ -2599,7 +2804,7 @@ pcnet_init(const device_t *info)
|
|
|
|
|
dev->base_address = device_get_config_hex16("base");
|
|
|
|
|
dev->base_irq = device_get_config_int("irq");
|
|
|
|
|
if (dev->is_vlb)
|
|
|
|
|
dev->dma_channel = 0;
|
|
|
|
|
dev->dma_channel = -1;
|
|
|
|
|
else
|
|
|
|
|
dev->dma_channel = device_get_config_int("dma");
|
|
|
|
|
pcnet_ioset(dev, dev->base_address, 0x20);
|
|
|
|
@@ -2611,7 +2816,7 @@ pcnet_init(const device_t *info)
|
|
|
|
|
dev->aPROM[3], dev->aPROM[4], dev->aPROM[5]);
|
|
|
|
|
|
|
|
|
|
pcnetlog(1, "%s: %s attached IO=0x%X IRQ=%d\n", dev->name,
|
|
|
|
|
dev->is_pci?"PCI":"ISA", dev->base_address, dev->base_irq);
|
|
|
|
|
dev->is_pci?"PCI":"VLB/ISA", dev->base_address, dev->base_irq);
|
|
|
|
|
|
|
|
|
|
/* Reset the board. */
|
|
|
|
|
pcnetHardReset(dev);
|
|
|
|
@@ -2619,6 +2824,11 @@ pcnet_init(const device_t *info)
|
|
|
|
|
/* Attach ourselves to the network module. */
|
|
|
|
|
network_attach(dev, dev->aPROM, pcnetReceiveNoSync, pcnetWaitReceiveAvail);
|
|
|
|
|
|
|
|
|
|
if (dev->board == DEV_AM79C973)
|
|
|
|
|
timer_add(&dev->timer_soft_int, pcnetTimerSoftInt, dev, 0);
|
|
|
|
|
|
|
|
|
|
timer_add(&dev->poll_timer, pcnetTimerCallback, dev, 0);
|
|
|
|
|
|
|
|
|
|
return(dev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -2630,7 +2840,13 @@ pcnet_close(void *priv)
|
|
|
|
|
|
|
|
|
|
pcnetlog(1, "%s: closed\n", dev->name);
|
|
|
|
|
|
|
|
|
|
if (dev) {
|
|
|
|
|
timer_disable(&dev->poll_timer);
|
|
|
|
|
|
|
|
|
|
free(dev);
|
|
|
|
|
dev = NULL;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2765,29 +2981,47 @@ static const device_config_t pcnet_vlb_config[] =
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const device_t pcnet_pci_device = {
|
|
|
|
|
"AMD PCnet-PCI",
|
|
|
|
|
DEVICE_PCI,
|
|
|
|
|
PCNET_PCI,
|
|
|
|
|
pcnet_init, pcnet_close, NULL,
|
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
|
pcnet_pci_config
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const device_t pcnet_isa_device = {
|
|
|
|
|
"AMD PCnet-ISA",
|
|
|
|
|
const device_t pcnet_am79c960_device = {
|
|
|
|
|
"AMD Am79c960 (PCnet-ISA) ",
|
|
|
|
|
DEVICE_AT | DEVICE_ISA,
|
|
|
|
|
PCNET_ISA,
|
|
|
|
|
DEV_AM79C960,
|
|
|
|
|
pcnet_init, pcnet_close, NULL,
|
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
|
pcnet_isa_config
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const device_t pcnet_vlb_device = {
|
|
|
|
|
"AMD PCnet-VL",
|
|
|
|
|
const device_t pcnet_am79c960_eb_device = {
|
|
|
|
|
"AMD Am79c960EB (PCnet-ISA) ",
|
|
|
|
|
DEVICE_AT | DEVICE_ISA,
|
|
|
|
|
DEV_AM79C960_EB,
|
|
|
|
|
pcnet_init, pcnet_close, NULL,
|
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
|
pcnet_isa_config
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const device_t pcnet_am79c960_vlb_device = {
|
|
|
|
|
"AMD Am79c960 (PCnet-VL) ",
|
|
|
|
|
DEVICE_VLB,
|
|
|
|
|
PCNET_VLB,
|
|
|
|
|
DEV_AM79C960_VLB,
|
|
|
|
|
pcnet_init, pcnet_close, NULL,
|
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
|
pcnet_vlb_config
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const device_t pcnet_am79c970a_device = {
|
|
|
|
|
"AMD Am79c970a (PCnet-PCI II)",
|
|
|
|
|
DEVICE_PCI,
|
|
|
|
|
DEV_AM79C970A,
|
|
|
|
|
pcnet_init, pcnet_close, NULL,
|
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
|
pcnet_pci_config
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const device_t pcnet_am79c973_device = {
|
|
|
|
|
"AMD Am79c973 (PCnet-FAST)",
|
|
|
|
|
DEVICE_PCI,
|
|
|
|
|
DEV_AM79C973,
|
|
|
|
|
pcnet_init, pcnet_close, NULL,
|
|
|
|
|
NULL, NULL, NULL,
|
|
|
|
|
pcnet_pci_config
|
|
|
|
|
};
|
|
|
|
|