summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2012-04-28 16:47:46 +0200
committerMichael Brown2012-04-28 20:10:41 +0200
commitf8bb40b002ccf38c90d5dfe181147d7e272fa51a (patch)
tree1dd47d39536912282995d4f970e550b4fcbf0f4c
parent[http] Fix typo in memory allocation (diff)
downloadipxe-f8bb40b002ccf38c90d5dfe181147d7e272fa51a.tar.gz
ipxe-f8bb40b002ccf38c90d5dfe181147d7e272fa51a.tar.xz
ipxe-f8bb40b002ccf38c90d5dfe181147d7e272fa51a.zip
[realtek] Support RTL8139 cards within generic Realtek driver
RTL8139C+ cards use essentially the same datapath as RTL8169, which is zerocopy and 64-bit capable. Older RTL8139 cards use a single receive ring buffer rather than a descriptor ring, but still share substantial amounts of functionality with RTL8169. Include support for RTL8139 cards within the generic Realtek driver, since there is no way to differentiate between RTL8139 and RTL8139C+ cards based on the PCI IDs alone. Many thanks to all the people who worked on the rtl8139 driver over the years. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/drivers/net/realtek.c351
-rw-r--r--src/drivers/net/realtek.h102
-rw-r--r--src/drivers/net/rtl8139.c596
3 files changed, 400 insertions, 649 deletions
diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c
index 5f111a68..c906f251 100644
--- a/src/drivers/net/realtek.c
+++ b/src/drivers/net/realtek.c
@@ -78,7 +78,9 @@ static int realtek_spi_read_bit ( struct bit_basher *basher,
uint8_t mask = realtek_eeprom_bits[bit_id];
uint8_t reg;
+ DBG_DISABLE ( DBGLVL_IO );
reg = readb ( rtl->regs + RTL_9346CR );
+ DBG_ENABLE ( DBGLVL_IO );
return ( reg & mask );
}
@@ -96,10 +98,12 @@ static void realtek_spi_write_bit ( struct bit_basher *basher,
uint8_t mask = realtek_eeprom_bits[bit_id];
uint8_t reg;
+ DBG_DISABLE ( DBGLVL_IO );
reg = readb ( rtl->regs + RTL_9346CR );
reg &= ~mask;
reg |= ( data & mask );
writeb ( reg, rtl->regs + RTL_9346CR );
+ DBG_ENABLE ( DBGLVL_IO );
}
/** SPI bit-bashing interface */
@@ -165,6 +169,10 @@ static int realtek_mii_read ( struct mii_interface *mii, unsigned int reg ) {
unsigned int i;
uint32_t value;
+ /* Fail if PHYAR register is not present */
+ if ( ! rtl->have_phy_regs )
+ return -ENOTSUP;
+
/* Initiate read */
writel ( RTL_PHYAR_VALUE ( 0, reg, 0 ), rtl->regs + RTL_PHYAR );
@@ -199,6 +207,10 @@ static int realtek_mii_write ( struct mii_interface *mii, unsigned int reg,
struct realtek_nic *rtl = container_of ( mii, struct realtek_nic, mii );
unsigned int i;
+ /* Fail if PHYAR register is not present */
+ if ( ! rtl->have_phy_regs )
+ return -ENOTSUP;
+
/* Initiate write */
writel ( RTL_PHYAR_VALUE ( RTL_PHYAR_FLAG, reg, data ),
rtl->regs + RTL_PHYAR );
@@ -253,10 +265,6 @@ static int realtek_reset ( struct realtek_nic *rtl ) {
continue;
}
- /* Enable PCI Dual Address Cycle (for 64-bit systems) */
- writew ( ( RTL_CPCR_DAC | RTL_CPCR_MULRW ),
- rtl->regs + RTL_CPCR );
-
return 0;
}
@@ -278,8 +286,23 @@ static int realtek_reset ( struct realtek_nic *rtl ) {
*/
static void realtek_check_link ( struct net_device *netdev ) {
struct realtek_nic *rtl = netdev->priv;
+ uint8_t phystatus;
+ uint8_t msr;
+ int link_up;
+
+ /* Determine link state */
+ if ( rtl->have_phy_regs ) {
+ phystatus = readb ( rtl->regs + RTL_PHYSTATUS );
+ link_up = ( phystatus & RTL_PHYSTATUS_LINKSTS );
+ DBGC ( rtl, "REALTEK %p PHY status is %02x\n", rtl, phystatus );
+ } else {
+ msr = readb ( rtl->regs + RTL_MSR );
+ link_up = ( ! ( msr & RTL_MSR_LINKB ) );
+ DBGC ( rtl, "REALTEK %p media status is %02x\n", rtl, msr );
+ }
- if ( readb ( rtl->regs + RTL_PHYSTATUS ) & RTL_PHYSTATUS_LINKSTS ) {
+ /* Report link state */
+ if ( link_up ) {
netdev_link_up ( netdev );
} else {
netdev_link_down ( netdev );
@@ -294,6 +317,74 @@ static void realtek_check_link ( struct net_device *netdev ) {
*/
/**
+ * Create receive buffer (legacy mode)
+ *
+ * @v rtl Realtek device
+ * @ret rc Return status code
+ */
+static int realtek_create_buffer ( struct realtek_nic *rtl ) {
+ size_t len = ( RTL_RXBUF_LEN + RTL_RXBUF_PAD );
+ physaddr_t address;
+ int rc;
+
+ /* Do nothing unless in legacy mode */
+ if ( ! rtl->legacy )
+ return 0;
+
+ /* Allocate buffer */
+ rtl->rx_buffer = malloc_dma ( len, RTL_RXBUF_ALIGN );
+ if ( ! rtl->rx_buffer ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ address = virt_to_bus ( rtl->rx_buffer );
+
+ /* Check that card can support address */
+ if ( address & ~0xffffffffULL ) {
+ DBGC ( rtl, "REALTEK %p cannot support 64-bit RX buffer "
+ "address\n", rtl );
+ rc = -ENOTSUP;
+ goto err_64bit;
+ }
+
+ /* Program buffer address */
+ writel ( address, rtl->regs + RTL_RBSTART );
+ DBGC ( rtl, "REALTEK %p receive buffer is at [%08llx,%08llx,%08llx)\n",
+ rtl, ( ( unsigned long long ) address ),
+ ( ( unsigned long long ) address + RTL_RXBUF_LEN ),
+ ( ( unsigned long long ) address + len ) );
+
+ return 0;
+
+ err_64bit:
+ free_dma ( rtl->rx_buffer, len );
+ rtl->rx_buffer = NULL;
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Destroy receive buffer (legacy mode)
+ *
+ * @v rtl Realtek device
+ */
+static void realtek_destroy_buffer ( struct realtek_nic *rtl ) {
+ size_t len = ( RTL_RXBUF_LEN + RTL_RXBUF_PAD );
+
+ /* Do nothing unless in legacy mode */
+ if ( ! rtl->legacy )
+ return;
+
+ /* Clear buffer address */
+ writel ( 0, rtl->regs + RTL_RBSTART );
+
+ /* Free buffer */
+ free_dma ( rtl->rx_buffer, len );
+ rtl->rx_buffer = NULL;
+ rtl->rx_offset = 0;
+}
+
+/**
* Create descriptor ring
*
* @v rtl Realtek device
@@ -304,6 +395,10 @@ static int realtek_create_ring ( struct realtek_nic *rtl,
struct realtek_ring *ring ) {
physaddr_t address;
+ /* Do nothing in legacy mode */
+ if ( rtl->legacy )
+ return 0;
+
/* Allocate descriptor ring */
ring->desc = malloc_dma ( ring->len, RTL_RING_ALIGN );
if ( ! ring->desc )
@@ -335,6 +430,10 @@ static int realtek_create_ring ( struct realtek_nic *rtl,
static void realtek_destroy_ring ( struct realtek_nic *rtl,
struct realtek_ring *ring ) {
+ /* Do nothing in legacy mode */
+ if ( rtl->legacy )
+ return;
+
/* Clear ring address */
writel ( 0, rtl->regs + ring->reg );
writel ( 0, rtl->regs + ring->reg + 4 );
@@ -358,6 +457,10 @@ static void realtek_refill_rx ( struct realtek_nic *rtl ) {
physaddr_t address;
int is_last;
+ /* Do nothing in legacy mode */
+ if ( rtl->legacy )
+ return;
+
while ( ( rtl->rx.prod - rtl->rx.cons ) < RTL_NUM_RX_DESC ) {
/* Allocate I/O buffer */
@@ -410,27 +513,36 @@ static int realtek_open ( struct net_device *netdev ) {
if ( ( rc = realtek_create_ring ( rtl, &rtl->rx ) ) != 0 )
goto err_create_rx;
- /* Configure MTU */
- writew ( RTL_RX_MAX_LEN, rtl->regs + RTL_RMS );
+ /* Create receive buffer */
+ if ( ( rc = realtek_create_buffer ( rtl ) ) != 0 )
+ goto err_create_buffer;
/* Accept all packets */
writel ( 0xffffffffUL, rtl->regs + RTL_MAR0 );
writel ( 0xffffffffUL, rtl->regs + RTL_MAR4 );
+
+ /* Enable transmitter and receiver. RTL8139 requires that
+ * this happens before writing to RCR.
+ */
+ writeb ( ( RTL_CR_TE | RTL_CR_RE ), rtl->regs + RTL_CR );
+
+ /* Configure receiver */
rcr = readl ( rtl->regs + RTL_RCR );
- writel ( ( rcr | RTL_RCR_AB | RTL_RCR_AM | RTL_RCR_APM | RTL_RCR_AAP ),
- rtl->regs + RTL_RCR );
+ rcr &= ~( RTL_RCR_RBLEN_MASK );
+ rcr |= ( RTL_RCR_RBLEN_DEFAULT | RTL_RCR_WRAP | RTL_RCR_AB |
+ RTL_RCR_AM | RTL_RCR_APM | RTL_RCR_AAP );
+ writel ( rcr, rtl->regs + RTL_RCR );
/* Fill receive ring */
realtek_refill_rx ( rtl );
- /* Enable transmitter and receiver */
- writeb ( ( RTL_CR_TE | RTL_CR_RE ), rtl->regs + RTL_CR );
-
/* Update link state */
realtek_check_link ( netdev );
return 0;
+ realtek_destroy_buffer ( rtl );
+ err_create_buffer:
realtek_destroy_ring ( rtl, &rtl->rx );
err_create_rx:
realtek_destroy_ring ( rtl, &rtl->tx );
@@ -450,6 +562,9 @@ static void realtek_close ( struct net_device *netdev ) {
/* Disable receiver and transmitter */
writeb ( 0, rtl->regs + RTL_CR );
+ /* Destroy receive buffer */
+ realtek_destroy_buffer ( rtl );
+
/* Destroy receive descriptor ring */
realtek_destroy_ring ( rtl, &rtl->rx );
@@ -485,24 +600,48 @@ static int realtek_transmit ( struct net_device *netdev,
return -ENOBUFS;
}
tx_idx = ( rtl->tx.prod++ % RTL_NUM_TX_DESC );
- is_last = ( tx_idx == ( RTL_NUM_TX_DESC - 1 ) );
- tx = &rtl->tx.desc[tx_idx];
- /* Populate transmit descriptor */
- address = virt_to_bus ( iobuf->data );
- tx->address = cpu_to_le64 ( address );
- tx->length = cpu_to_le16 ( iob_len ( iobuf ) );
- wmb();
- tx->flags = ( cpu_to_le16 ( RTL_DESC_OWN | RTL_DESC_FS | RTL_DESC_LS ) |
- ( is_last ? cpu_to_le16 ( RTL_DESC_EOR ) : 0 ) );
- wmb();
+ /* Transmit packet */
+ if ( rtl->legacy ) {
- /* Notify card that there are packets ready to transmit */
- writeb ( RTL_TPPOLL_NPQ, rtl->regs + RTL_TPPOLL );
+ /* Pad and align packet */
+ iob_pad ( iobuf, ETH_ZLEN );
+ address = virt_to_bus ( iobuf->data );
+
+ /* Check that card can support address */
+ if ( address & ~0xffffffffULL ) {
+ DBGC ( rtl, "REALTEK %p cannot support 64-bit TX "
+ "buffer address\n", rtl );
+ return -ENOTSUP;
+ }
+
+ /* Add to transmit ring */
+ writel ( address, rtl->regs + RTL_TSAD ( tx_idx ) );
+ writel ( ( RTL_TSD_ERTXTH_DEFAULT | iob_len ( iobuf ) ),
+ rtl->regs + RTL_TSD ( tx_idx ) );
+
+ } else {
+
+ /* Populate transmit descriptor */
+ address = virt_to_bus ( iobuf->data );
+ is_last = ( tx_idx == ( RTL_NUM_TX_DESC - 1 ) );
+ tx = &rtl->tx.desc[tx_idx];
+ tx->address = cpu_to_le64 ( address );
+ tx->length = cpu_to_le16 ( iob_len ( iobuf ) );
+ wmb();
+ tx->flags = ( cpu_to_le16 ( RTL_DESC_OWN | RTL_DESC_FS |
+ RTL_DESC_LS ) |
+ ( is_last ? cpu_to_le16 ( RTL_DESC_EOR ) : 0 ) );
+ wmb();
+
+ /* Notify card that there are packets ready to transmit */
+ writeb ( RTL_TPPOLL_NPQ, rtl->regs + rtl->tppoll );
+ }
DBGC2 ( rtl, "REALTEK %p TX %d is [%llx,%llx)\n", rtl, tx_idx,
- ( ( unsigned long long ) address ),
- ( ( unsigned long long ) address + iob_len ( iobuf ) ) );
+ ( ( unsigned long long ) virt_to_bus ( iobuf->data ) ),
+ ( ( ( unsigned long long ) virt_to_bus ( iobuf->data ) ) +
+ iob_len ( iobuf ) ) );
return 0;
}
@@ -522,11 +661,22 @@ static void realtek_poll_tx ( struct net_device *netdev ) {
/* Get next transmit descriptor */
tx_idx = ( rtl->tx.cons % RTL_NUM_TX_DESC );
- tx = &rtl->tx.desc[tx_idx];
/* Stop if descriptor is still in use */
- if ( tx->flags & cpu_to_le16 ( RTL_DESC_OWN ) )
- return;
+ if ( rtl->legacy ) {
+
+ /* Check ownership bit in transmit status register */
+ if ( ! ( readl ( rtl->regs + RTL_TSD ( tx_idx ) ) &
+ RTL_TSD_OWN ) )
+ return;
+
+ } else {
+
+ /* Check ownership bit in descriptor */
+ tx = &rtl->tx.desc[tx_idx];
+ if ( tx->flags & cpu_to_le16 ( RTL_DESC_OWN ) )
+ return;
+ }
DBGC2 ( rtl, "REALTEK %p TX %d complete\n", rtl, tx_idx );
@@ -537,6 +687,59 @@ static void realtek_poll_tx ( struct net_device *netdev ) {
}
/**
+ * Poll for received packets (legacy mode)
+ *
+ * @v netdev Network device
+ */
+static void realtek_legacy_poll_rx ( struct net_device *netdev ) {
+ struct realtek_nic *rtl = netdev->priv;
+ struct realtek_legacy_header *rx;
+ struct io_buffer *iobuf;
+ size_t len;
+
+ /* Check for received packets */
+ while ( ! ( readb ( rtl->regs + RTL_CR ) & RTL_CR_BUFE ) ) {
+
+ /* Extract packet from receive buffer */
+ rx = ( rtl->rx_buffer + rtl->rx_offset );
+ len = le16_to_cpu ( rx->length );
+ if ( rx->status & cpu_to_le16 ( RTL_STAT_ROK ) ) {
+
+ DBGC2 ( rtl, "REALTEK %p RX offset %x+%zx\n",
+ rtl, rtl->rx_offset, len );
+
+ /* Allocate I/O buffer */
+ iobuf = alloc_iob ( len );
+ if ( ! iobuf ) {
+ netdev_rx_err ( netdev, NULL, -ENOMEM );
+ /* Leave packet for next poll */
+ break;
+ }
+
+ /* Copy data to I/O buffer */
+ memcpy ( iob_put ( iobuf, len ), rx->data, len );
+ iob_unput ( iobuf, 4 /* strip CRC */ );
+
+ /* Hand off to network stack */
+ netdev_rx ( netdev, iobuf );
+
+ } else {
+
+ DBGC ( rtl, "REALTEK %p RX offset %x+%zx error %04x\n",
+ rtl, rtl->rx_offset, len,
+ le16_to_cpu ( rx->status ) );
+ netdev_rx_err ( netdev, NULL, -EIO );
+ }
+
+ /* Update buffer offset */
+ rtl->rx_offset = ( rtl->rx_offset + sizeof ( *rx ) + len );
+ rtl->rx_offset = ( ( rtl->rx_offset + 3 ) & ~3 );
+ rtl->rx_offset = ( rtl->rx_offset % RTL_RXBUF_LEN );
+ writew ( ( rtl->rx_offset - 16 ), rtl->regs + RTL_CAPR );
+ }
+}
+
+/**
* Poll for received packets
*
* @v netdev Network device
@@ -548,6 +751,12 @@ static void realtek_poll_rx ( struct net_device *netdev ) {
unsigned int rx_idx;
size_t len;
+ /* Poll receive buffer if in legacy mode */
+ if ( rtl->legacy ) {
+ realtek_legacy_poll_rx ( netdev );
+ return;
+ }
+
/* Check for received packets */
while ( rtl->rx.cons != rtl->rx.prod ) {
@@ -642,6 +851,51 @@ static struct net_device_operations realtek_operations = {
*/
/**
+ * Detect device type
+ *
+ * @v rtl Realtek device
+ */
+static void realtek_detect ( struct realtek_nic *rtl ) {
+ uint16_t rms;
+ uint16_t check_rms;
+ uint16_t cpcr;
+ uint16_t check_cpcr;
+
+ /* The RX Packet Maximum Size register is present only on
+ * 8169. Try to set to our intended MTU.
+ */
+ rms = RTL_RX_MAX_LEN;
+ writew ( rms, rtl->regs + RTL_RMS );
+ check_rms = readw ( rtl->regs + RTL_RMS );
+
+ /* The C+ Command register is present only on 8169 and 8139C+.
+ * Try to enable C+ mode and PCI Dual Address Cycle (for
+ * 64-bit systems), if supported.
+ */
+ cpcr = ( RTL_CPCR_DAC | RTL_CPCR_MULRW | RTL_CPCR_CPRX |
+ RTL_CPCR_CPTX );
+ writew ( cpcr, rtl->regs + RTL_CPCR );
+ check_cpcr = readw ( rtl->regs + RTL_CPCR );
+
+ /* Detect device type */
+ if ( check_rms == rms ) {
+ DBGC ( rtl, "REALTEK %p appears to be an RTL8169\n", rtl );
+ rtl->have_phy_regs = 1;
+ rtl->tppoll = RTL_TPPOLL_8169;
+ } else {
+ if ( check_cpcr == cpcr ) {
+ DBGC ( rtl, "REALTEK %p appears to be an RTL8139C+\n",
+ rtl );
+ rtl->tppoll = RTL_TPPOLL_8139CP;
+ } else {
+ DBGC ( rtl, "REALTEK %p appears to be an RTL8139\n",
+ rtl );
+ rtl->legacy = 1;
+ }
+ }
+}
+
+/**
* Probe PCI device
*
* @v pci PCI device
@@ -677,6 +931,9 @@ static int realtek_probe ( struct pci_device *pci ) {
if ( ( rc = realtek_reset ( rtl ) ) != 0 )
goto err_reset;
+ /* Detect device type */
+ realtek_detect ( rtl );
+
/* Initialise EEPROM */
realtek_init_eeprom ( netdev );
@@ -700,7 +957,8 @@ static int realtek_probe ( struct pci_device *pci ) {
/* Initialise and reset MII interface */
mii_init ( &rtl->mii, &realtek_mii_operations );
- if ( ( rc = mii_reset ( &rtl->mii ) ) != 0 ) {
+ if ( rtl->have_phy_regs &&
+ ( ( rc = mii_reset ( &rtl->mii ) ) != 0 ) ) {
DBGC ( rtl, "REALTEK %p could not reset MII: %s\n",
rtl, strerror ( rc ) );
goto err_mii_reset;
@@ -761,16 +1019,37 @@ static void realtek_remove ( struct pci_device *pci ) {
/** Realtek PCI device IDs */
static struct pci_device_id realtek_nics[] = {
- PCI_ROM ( 0x10ec, 0x8129, "r8129", "RTL-8129", 0 ),
- PCI_ROM ( 0x10ec, 0x8136, "r8136", "RTL8101E/RTL8102E", 0 ),
- PCI_ROM ( 0x10ec, 0x8167, "r8167", "RTL-8110SC/8169SC", 0 ),
- PCI_ROM ( 0x10ec, 0x8168, "r8168", "RTL8111/8168B", 0 ),
- PCI_ROM ( 0x10ec, 0x8169, "r8169", "RTL-8169", 0 ),
+ PCI_ROM ( 0x0001, 0x8168, "clone8169", "Cloned 8169", 0 ),
+ PCI_ROM ( 0x018a, 0x0106, "fpc0106tx", "LevelOne FPC-0106TX", 0 ),
+ PCI_ROM ( 0x021b, 0x8139, "hne300", "Compaq HNE-300", 0 ),
+ PCI_ROM ( 0x02ac, 0x1012, "s1012", "SpeedStream 1012", 0 ),
+ PCI_ROM ( 0x0357, 0x000a, "ttpmon", "TTTech TTP-Monitoring", 0 ),
+ PCI_ROM ( 0x10ec, 0x8129, "rtl8129", "RTL-8129", 0 ),
+ PCI_ROM ( 0x10ec, 0x8136, "rtl8136", "RTL8101E/RTL8102E", 0 ),
+ PCI_ROM ( 0x10ec, 0x8138, "rtl8138", "RT8139 (B/C)", 0 ),
+ PCI_ROM ( 0x10ec, 0x8139, "rtl8139", "RTL-8139/8139C/8139C+", 0 ),
+ PCI_ROM ( 0x10ec, 0x8167, "rtl8167", "RTL-8110SC/8169SC", 0 ),
+ PCI_ROM ( 0x10ec, 0x8168, "rtl8168", "RTL8111/8168B", 0 ),
+ PCI_ROM ( 0x10ec, 0x8169, "rtl8169", "RTL-8169", 0 ),
+ PCI_ROM ( 0x1113, 0x1211, "smc1211", "SMC2-1211TX", 0 ),
+ PCI_ROM ( 0x1186, 0x1300, "dfe538", "DFE530TX+/DFE538TX", 0 ),
+ PCI_ROM ( 0x1186, 0x1340, "dfe690", "DFE-690TXD", 0 ),
PCI_ROM ( 0x1186, 0x4300, "dge528t", "DGE-528T", 0 ),
+ PCI_ROM ( 0x11db, 0x1234, "sega8139", "Sega Enterprises 8139", 0 ),
+ PCI_ROM ( 0x1259, 0xa117, "allied8139", "Allied Telesyn 8139", 0 ),
+ PCI_ROM ( 0x1259, 0xa11e, "allied81xx", "Allied Telesyn 81xx", 0 ),
PCI_ROM ( 0x1259, 0xc107, "allied8169", "Allied Telesyn 8169", 0 ),
+ PCI_ROM ( 0x126c, 0x1211, "northen8139","Northern Telecom 8139", 0 ),
+ PCI_ROM ( 0x13d1, 0xab06, "fe2000vx", "Abocom FE2000VX", 0 ),
+ PCI_ROM ( 0x1432, 0x9130, "edi8139", "Edimax 8139", 0 ),
+ PCI_ROM ( 0x14ea, 0xab06, "fnw3603tx", "Planex FNW-3603-TX", 0 ),
+ PCI_ROM ( 0x14ea, 0xab07, "fnw3800tx", "Planex FNW-3800-TX", 0 ),
+ PCI_ROM ( 0x1500, 0x1360, "delta8139", "Delta Electronics 8139", 0 ),
PCI_ROM ( 0x16ec, 0x0116, "usr997902", "USR997902", 0 ),
PCI_ROM ( 0x1737, 0x1032, "linksys8169","Linksys 8169", 0 ),
- PCI_ROM ( 0x0001, 0x8168, "clone8169", "Cloned 8169", 0 ),
+ PCI_ROM ( 0x1743, 0x8139, "rolf100", "Peppercorn ROL/F-100", 0 ),
+ PCI_ROM ( 0x4033, 0x1360, "addron8139", "Addtron 8139", 0 ),
+ PCI_ROM ( 0xffff, 0x8139, "clonse8139", "Cloned 8139", 0 ),
};
/** Realtek PCI driver */
diff --git a/src/drivers/net/realtek.h b/src/drivers/net/realtek.h
index aad4b34c..1c6bc544 100644
--- a/src/drivers/net/realtek.h
+++ b/src/drivers/net/realtek.h
@@ -49,6 +49,22 @@ enum realtek_descriptor_flags {
/** Descriptor ring alignment */
#define RTL_RING_ALIGN 256
+/** A legacy mode receive packet header */
+struct realtek_legacy_header {
+ /** Status */
+ uint16_t status;
+ /** Length */
+ uint16_t length;
+ /** Packet data */
+ uint8_t data[0];
+} __attribute__ (( packed ));
+
+/** Legacy mode status bits */
+enum realtek_legacy_status {
+ /** Received OK */
+ RTL_STAT_ROK = 0x0001,
+};
+
/** ID Register 0 (6 bytes) */
#define RTL_IDR0 0x00
@@ -58,43 +74,75 @@ enum realtek_descriptor_flags {
/** Multicast Register 4 (dword) */
#define RTL_MAR4 0x0c
+/** Transmit Status of Descriptor N (dword, 8139 only) */
+#define RTL_TSD(n) ( 0x10 + 4 * (n) )
+#define RTL_TSD_ERTXTH(x) ( (x) << 16 ) /**< Early TX threshold */
+#define RTL_TSD_ERTXTH_DEFAULT RTL_TSD_ERTXTH ( 256 / 32 )
+#define RTL_TSD_OWN 0x00002000UL /**< Ownership */
+
+/** Transmit Start Address of Descriptor N (dword, 8139 only) */
+#define RTL_TSAD(n) ( 0x20 + 4 * (n) )
+
/** Transmit Normal Priority Descriptors (qword) */
#define RTL_TNPDS 0x20
-/** Number of transmit descriptors */
+/** Number of transmit descriptors
+ *
+ * This is a hardware limit when using legacy mode.
+ */
#define RTL_NUM_TX_DESC 4
+/** Receive Buffer Start Address (dword, 8139 only) */
+#define RTL_RBSTART 0x30
+
+/** Receive buffer length */
+#define RTL_RXBUF_LEN 8192
+
+/** Receive buffer padding */
+#define RTL_RXBUF_PAD 2038 /* Allow space for WRAP */
+
+/** Receive buffer alignment */
+#define RTL_RXBUF_ALIGN 16
+
/** Command Register (byte) */
#define RTL_CR 0x37
#define RTL_CR_RST 0x10 /**< Reset */
#define RTL_CR_RE 0x08 /**< Receiver Enable */
#define RTL_CR_TE 0x04 /**< Transmit Enable */
+#define RTL_CR_BUFE 0x01 /**< Receive buffer empty */
/** Maximum time to wait for a reset, in milliseconds */
#define RTL_RESET_MAX_WAIT_MS 100
-/** Transmit Priority Polling Register (byte) */
-#define RTL_TPPOLL 0x38
+/** Current Address of Packet Read (word, 8139 only) */
+#define RTL_CAPR 0x38
+
+/** Transmit Priority Polling Register (byte, 8169 only) */
+#define RTL_TPPOLL_8169 0x38
#define RTL_TPPOLL_NPQ 0x40 /**< Normal Priority Queue Polling */
/** Interrupt Mask Register (word) */
#define RTL_IMR 0x3c
-#define RTL_IRQ_PUN_LINKCHG 0x20 /**< Packet underrun / link change */
-#define RTL_IRQ_TER 0x08 /**< Transmit error */
-#define RTL_IRQ_TOK 0x04 /**< Transmit OK */
-#define RTL_IRQ_RER 0x02 /**< Receive error */
-#define RTL_IRQ_ROK 0x01 /**< Receive OK */
+#define RTL_IRQ_PUN_LINKCHG 0x0020 /**< Packet underrun / link change */
+#define RTL_IRQ_TER 0x0008 /**< Transmit error */
+#define RTL_IRQ_TOK 0x0004 /**< Transmit OK */
+#define RTL_IRQ_RER 0x0002 /**< Receive error */
+#define RTL_IRQ_ROK 0x0001 /**< Receive OK */
/** Interrupt Status Register (word) */
#define RTL_ISR 0x3e
/** Receive (Rx) Configuration Register (dword) */
#define RTL_RCR 0x44
-#define RTL_RCR_9356SEL 0x40 /**< EEPROM is a 93C56 */
-#define RTL_RCR_AB 0x08 /**< Accept broadcast packets */
-#define RTL_RCR_AM 0x04 /**< Accept multicast packets */
-#define RTL_RCR_APM 0x02 /**< Accept physical match packets */
-#define RTL_RCR_AAP 0x01 /**< Accept all packets */
+#define RTL_RCR_RBLEN(x) ( (x) << 11 ) /**< Receive buffer length */
+#define RTL_RCR_RBLEN_MASK RTL_RCR_RBLEN ( 0x3 )
+#define RTL_RCR_RBLEN_DEFAULT RTL_RCR_RBLEN ( 0 /* 8kB */ )
+#define RTL_RCR_WRAP 0x00000080UL /**< Overrun receive buffer */
+#define RTL_RCR_9356SEL 0x00000040UL /**< EEPROM is a 93C56 */
+#define RTL_RCR_AB 0x00000008UL /**< Accept broadcast packets */
+#define RTL_RCR_AM 0x00000004UL /**< Accept multicast packets */
+#define RTL_RCR_APM 0x00000002UL /**< Accept physical match */
+#define RTL_RCR_AAP 0x00000001UL /**< Accept all packets */
/** 93C46 (93C56) Command Register (byte) */
#define RTL_9346CR 0x50
@@ -118,7 +166,11 @@ enum realtek_descriptor_flags {
#define RTL_CONFIG1 0x52
#define RTL_CONFIG1_VPD 0x02 /**< Vital Product Data enabled */
-/** PHY Access Register (dword) */
+/** Media Status Register (byte, 8139 only) */
+#define RTL_MSR 0x58
+#define RTL_MSR_LINKB 0x04 /**< Inverse of link status */
+
+/** PHY Access Register (dword, 8169 only) */
#define RTL_PHYAR 0x60
#define RTL_PHYAR_FLAG 0x80000000UL /**< Read/write flag */
@@ -131,17 +183,22 @@ enum realtek_descriptor_flags {
/** Maximum time to wait for PHY access, in microseconds */
#define RTL_MII_MAX_WAIT_US 500
-/** PHY (GMII, MII, or TBI) Status Register (byte) */
+/** PHY (GMII, MII, or TBI) Status Register (byte, 8169 only) */
#define RTL_PHYSTATUS 0x6c
#define RTL_PHYSTATUS_LINKSTS 0x02 /**< Link ok */
+/** Transmit Priority Polling Register (byte, 8139C+ only) */
+#define RTL_TPPOLL_8139CP 0xd9
+
/** RX Packet Maximum Size Register (word) */
#define RTL_RMS 0xda
/** C+ Command Register (word) */
#define RTL_CPCR 0xe0
-#define RTL_CPCR_DAC 0x10 /**< PCI Dual Address Cycle Enable */
-#define RTL_CPCR_MULRW 0x08 /**< PCI Multiple Read/Write Enable */
+#define RTL_CPCR_DAC 0x0010 /**< PCI Dual Address Cycle Enable */
+#define RTL_CPCR_MULRW 0x0008 /**< PCI Multiple Read/Write Enable */
+#define RTL_CPCR_CPRX 0x0002 /**< C+ receive enable */
+#define RTL_CPCR_CPTX 0x0001 /**< C+ transmit enable */
/** Receive Descriptor Start Address Register (qword) */
#define RTL_RDSAR 0xe4
@@ -194,12 +251,23 @@ struct realtek_nic {
/** MII interface */
struct mii_interface mii;
+ /** Legacy datapath mode */
+ int legacy;
+ /** PHYAR and PHYSTATUS registers are present */
+ int have_phy_regs;
+ /** TPPoll register offset */
+ unsigned int tppoll;
+
/** Transmit descriptor ring */
struct realtek_ring tx;
/** Receive descriptor ring */
struct realtek_ring rx;
/** Receive I/O buffers */
struct io_buffer *rx_iobuf[RTL_NUM_RX_DESC];
+ /** Receive buffer (legacy mode) */
+ void *rx_buffer;
+ /** Offset within receive buffer (legacy mode) */
+ unsigned int rx_offset;
};
#endif /* _REALTEK_H */
diff --git a/src/drivers/net/rtl8139.c b/src/drivers/net/rtl8139.c
deleted file mode 100644
index 2da8223a..00000000
--- a/src/drivers/net/rtl8139.c
+++ /dev/null
@@ -1,596 +0,0 @@
-/* rtl8139.c - etherboot driver for the Realtek 8139 chipset
-
- ported from the linux driver written by Donald Becker
- by Rainer Bawidamann (Rainer.Bawidamann@informatik.uni-ulm.de) 1999
-
- This software may be used and distributed according to the terms
- of the GNU Public License, incorporated herein by reference.
-
- changes to the original driver:
- - removed support for interrupts, switching to polling mode (yuck!)
- - removed support for the 8129 chip (external MII)
-
-*/
-
-FILE_LICENCE ( GPL_ANY );
-
-/*********************************************************************/
-/* Revision History */
-/*********************************************************************/
-
-/*
- 27 May 2006 mcb30@users.sourceforge.net (Michael Brown)
- Rewrote to use the new net driver API, the updated PCI API, and
- the generic three-wire serial device support for EEPROM access.
-
- 28 Dec 2002 ken_yap@users.sourceforge.net (Ken Yap)
- Put in virt_to_bus calls to allow Etherboot relocation.
-
- 06 Apr 2001 ken_yap@users.sourceforge.net (Ken Yap)
- Following email from Hyun-Joon Cha, added a disable routine, otherwise
- NIC remains live and can crash the kernel later.
-
- 4 Feb 2000 espenlaub@informatik.uni-ulm.de (Klaus Espenlaub)
- Shuffled things around, removed the leftovers from the 8129 support
- that was in the Linux driver and added a bit more 8139 definitions.
- Moved the 8K receive buffer to a fixed, available address outside the
- 0x98000-0x9ffff range. This is a bit of a hack, but currently the only
- way to make room for the Etherboot features that need substantial amounts
- of code like the ANSI console support. Currently the buffer is just below
- 0x10000, so this even conforms to the tagged boot image specification,
- which reserves the ranges 0x00000-0x10000 and 0x98000-0xA0000. My
- interpretation of this "reserved" is that Etherboot may do whatever it
- likes, as long as its environment is kept intact (like the BIOS
- variables). Hopefully fixed rtl_poll() once and for all. The symptoms
- were that if Etherboot was left at the boot menu for several minutes, the
- first eth_poll failed. Seems like I am the only person who does this.
- First of all I fixed the debugging code and then set out for a long bug
- hunting session. It took me about a week full time work - poking around
- various places in the driver, reading Don Becker's and Jeff Garzik's Linux
- driver and even the FreeBSD driver (what a piece of crap!) - and
- eventually spotted the nasty thing: the transmit routine was acknowledging
- each and every interrupt pending, including the RxOverrun and RxFIFIOver
- interrupts. This confused the RTL8139 thoroughly. It destroyed the
- Rx ring contents by dumping the 2K FIFO contents right where we wanted to
- get the next packet. Oh well, what fun.
-
- 18 Jan 2000 mdc@etherboot.org (Marty Connor)
- Drastically simplified error handling. Basically, if any error
- in transmission or reception occurs, the card is reset.
- Also, pointed all transmit descriptors to the same buffer to
- save buffer space. This should decrease driver size and avoid
- corruption because of exceeding 32K during runtime.
-
- 28 Jul 1999 (Matthias Meixner - meixner@rbg.informatik.tu-darmstadt.de)
- rtl_poll was quite broken: it used the RxOK interrupt flag instead
- of the RxBufferEmpty flag which often resulted in very bad
- transmission performace - below 1kBytes/s.
-
-*/
-
-#include <stdint.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ipxe/io.h>
-#include <errno.h>
-#include <unistd.h>
-#include <byteswap.h>
-#include <ipxe/pci.h>
-#include <ipxe/if_ether.h>
-#include <ipxe/ethernet.h>
-#include <ipxe/iobuf.h>
-#include <ipxe/netdevice.h>
-#include <ipxe/spi_bit.h>
-#include <ipxe/threewire.h>
-#include <ipxe/nvo.h>
-
-#define TX_RING_SIZE 4
-#define TX_MAX_LEN 8192
-
-struct rtl8139_tx {
- unsigned int next;
- struct io_buffer *iobuf[TX_RING_SIZE];
-};
-
-struct rtl8139_rx {
- void *ring;
- unsigned int offset;
-};
-
-struct rtl8139_nic {
- unsigned short ioaddr;
- struct rtl8139_tx tx;
- struct rtl8139_rx rx;
- struct spi_bit_basher spibit;
- struct spi_device eeprom;
- struct nvo_block nvo;
-};
-
-/* Tuning Parameters */
-#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
-#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */
-#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */
-#define TX_DMA_BURST 4 /* Calculate as 16<<val. */
-#define TX_IPG 3 /* This is the only valid value */
-#define RX_BUF_LEN_IDX 0 /* 0, 1, 2 is allowed - 8,16,32K rx buffer */
-#define RX_BUF_LEN ( (8192 << RX_BUF_LEN_IDX) )
-#define RX_BUF_PAD 4
-
-/* Symbolic offsets to registers. */
-enum RTL8139_registers {
- MAC0=0, /* Ethernet hardware address. */
- MAR0=8, /* Multicast filter. */
- TxStatus0=0x10, /* Transmit status (four 32bit registers). */
- TxAddr0=0x20, /* Tx descriptors (also four 32bit). */
- RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36,
- ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A,
- IntrMask=0x3C, IntrStatus=0x3E,
- TxConfig=0x40, RxConfig=0x44,
- Timer=0x48, /* general-purpose counter. */
- RxMissed=0x4C, /* 24 bits valid, write clears. */
- Cfg9346=0x50, Config0=0x51, Config1=0x52,
- TimerIntrReg=0x54, /* intr if gp counter reaches this value */
- MediaStatus=0x58,
- Config3=0x59,
- MultiIntr=0x5C,
- RevisionID=0x5E, /* revision of the RTL8139 chip */
- TxSummary=0x60,
- MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68,
- NWayExpansion=0x6A,
- DisconnectCnt=0x6C, FalseCarrierCnt=0x6E,
- NWayTestReg=0x70,
- RxCnt=0x72, /* packet received counter */
- CSCR=0x74, /* chip status and configuration register */
- PhyParm1=0x78,TwisterParm=0x7c,PhyParm2=0x80, /* undocumented */
- /* from 0x84 onwards are a number of power management/wakeup frame
- * definitions we will probably never need to know about. */
-};
-
-enum RxEarlyStatusBits {
- ERGood=0x08, ERBad=0x04, EROVW=0x02, EROK=0x01
-};
-
-enum ChipCmdBits {
- CmdReset=0x10, CmdRxEnb=0x08, CmdTxEnb=0x04, RxBufEmpty=0x01, };
-
-enum IntrMaskBits {
- SERR=0x8000, TimeOut=0x4000, LenChg=0x2000,
- FOVW=0x40, PUN_LinkChg=0x20, RXOVW=0x10,
- TER=0x08, TOK=0x04, RER=0x02, ROK=0x01
-};
-
-/* Interrupt register bits, using my own meaningful names. */
-enum IntrStatusBits {
- PCIErr=0x8000, PCSTimeout=0x4000, CableLenChange= 0x2000,
- RxFIFOOver=0x40, RxUnderrun=0x20, RxOverflow=0x10,
- TxErr=0x08, TxOK=0x04, RxErr=0x02, RxOK=0x01,
-};
-enum TxStatusBits {
- TxHostOwns=0x2000, TxUnderrun=0x4000, TxStatOK=0x8000,
- TxOutOfWindow=0x20000000, TxAborted=0x40000000,
- TxCarrierLost=0x80000000,
-};
-enum RxStatusBits {
- RxMulticast=0x8000, RxPhysical=0x4000, RxBroadcast=0x2000,
- RxBadSymbol=0x0020, RxRunt=0x0010, RxTooLong=0x0008, RxCRCErr=0x0004,
- RxBadAlign=0x0002, RxStatusOK=0x0001,
-};
-
-enum MediaStatusBits {
- MSRTxFlowEnable=0x80, MSRRxFlowEnable=0x40, MSRSpeed10=0x08,
- MSRLinkFail=0x04, MSRRxPauseFlag=0x02, MSRTxPauseFlag=0x01,
-};
-
-enum MIIBMCRBits {
- BMCRReset=0x8000, BMCRSpeed100=0x2000, BMCRNWayEnable=0x1000,
- BMCRRestartNWay=0x0200, BMCRDuplex=0x0100,
-};
-
-enum CSCRBits {
- CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800,
- CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0,
- CSCR_LinkDownCmd=0x0f3c0,
-};
-
-enum RxConfigBits {
- RxCfgWrap=0x80,
- Eeprom9356=0x40,
- AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08,
- AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01,
-};
-
-enum Config1Bits {
- VPDEnable=0x02,
-};
-
-/* EEPROM access */
-#define EE_M1 0x80 /* Mode select bit 1 */
-#define EE_M0 0x40 /* Mode select bit 0 */
-#define EE_CS 0x08 /* EEPROM chip select */
-#define EE_SK 0x04 /* EEPROM shift clock */
-#define EE_DI 0x02 /* Data in */
-#define EE_DO 0x01 /* Data out */
-
-/* Offsets within EEPROM (these are word offsets) */
-#define EE_MAC 7
-
-static const uint8_t rtl_ee_bits[] = {
- [SPI_BIT_SCLK] = EE_SK,
- [SPI_BIT_MOSI] = EE_DI,
- [SPI_BIT_MISO] = EE_DO,
- [SPI_BIT_SS(0)] = ( EE_CS | EE_M1 ),
-};
-
-static int rtl_spi_read_bit ( struct bit_basher *basher,
- unsigned int bit_id ) {
- struct rtl8139_nic *rtl = container_of ( basher, struct rtl8139_nic,
- spibit.basher );
- uint8_t mask = rtl_ee_bits[bit_id];
- uint8_t eereg;
-
- eereg = inb ( rtl->ioaddr + Cfg9346 );
- return ( eereg & mask );
-}
-
-static void rtl_spi_write_bit ( struct bit_basher *basher,
- unsigned int bit_id, unsigned long data ) {
- struct rtl8139_nic *rtl = container_of ( basher, struct rtl8139_nic,
- spibit.basher );
- uint8_t mask = rtl_ee_bits[bit_id];
- uint8_t eereg;
-
- eereg = inb ( rtl->ioaddr + Cfg9346 );
- eereg &= ~mask;
- eereg |= ( data & mask );
- outb ( eereg, rtl->ioaddr + Cfg9346 );
-}
-
-static struct bit_basher_operations rtl_basher_ops = {
- .read = rtl_spi_read_bit,
- .write = rtl_spi_write_bit,
-};
-
-/**
- * Set up for EEPROM access
- *
- * @v netdev Net device
- */
-static void rtl_init_eeprom ( struct net_device *netdev ) {
- struct rtl8139_nic *rtl = netdev->priv;
- int ee9356;
- int vpd;
-
- /* Initialise three-wire bus */
- rtl->spibit.basher.op = &rtl_basher_ops;
- rtl->spibit.bus.mode = SPI_MODE_THREEWIRE;
- init_spi_bit_basher ( &rtl->spibit );
-
- /* Detect EEPROM type and initialise three-wire device */
- ee9356 = ( inw ( rtl->ioaddr + RxConfig ) & Eeprom9356 );
- if ( ee9356 ) {
- DBGC ( rtl, "rtl8139 %p EEPROM is an AT93C56\n", rtl );
- init_at93c56 ( &rtl->eeprom, 16 );
- } else {
- DBGC ( rtl, "rtl8139 %p EEPROM is an AT93C46\n", rtl );
- init_at93c46 ( &rtl->eeprom, 16 );
- }
- rtl->eeprom.bus = &rtl->spibit.bus;
-
- /* Initialise space for non-volatile options, if available
- *
- * We use offset 0x40 (i.e. address 0x20), length 0x40. This
- * block is marked as VPD in the rtl8139 datasheets, so we use
- * it only if we detect that the card is not supporting VPD.
- */
- vpd = ( inw ( rtl->ioaddr + Config1 ) & VPDEnable );
- if ( vpd ) {
- DBGC ( rtl, "rtl8139 %p EEPROM in use for VPD; cannot use "
- "for options\n", rtl );
- } else {
- nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, 0x20, 0x40, NULL,
- &netdev->refcnt );
- }
-}
-
-/**
- * Reset NIC
- *
- * @v netdev Net device
- *
- * Issues a hardware reset and waits for the reset to complete.
- */
-static void rtl_reset ( struct net_device *netdev ) {
- struct rtl8139_nic *rtl = netdev->priv;
-
- /* Reset chip */
- outb ( CmdReset, rtl->ioaddr + ChipCmd );
- mdelay ( 10 );
- memset ( &rtl->tx, 0, sizeof ( rtl->tx ) );
- rtl->rx.offset = 0;
-}
-
-/**
- * Open NIC
- *
- * @v netdev Net device
- * @ret rc Return status code
- */
-static int rtl_open ( struct net_device *netdev ) {
- struct rtl8139_nic *rtl = netdev->priv;
- int i;
-
- /* Program the MAC address */
- for ( i = 0 ; i < ETH_ALEN ; i++ )
- outb ( netdev->ll_addr[i], rtl->ioaddr + MAC0 + i );
-
- /* Set up RX ring */
- rtl->rx.ring = malloc ( RX_BUF_LEN + RX_BUF_PAD );
- if ( ! rtl->rx.ring )
- return -ENOMEM;
- outl ( virt_to_bus ( rtl->rx.ring ), rtl->ioaddr + RxBuf );
- DBGC ( rtl, "rtl8139 %p RX ring at %lx\n",
- rtl, virt_to_bus ( rtl->rx.ring ) );
-
- /* Enable TX and RX */
- outb ( ( CmdRxEnb | CmdTxEnb ), rtl->ioaddr + ChipCmd );
- outl ( ( ( RX_FIFO_THRESH << 13 ) | ( RX_BUF_LEN_IDX << 11 ) |
- ( RX_DMA_BURST << 8 ) | AcceptBroadcast | AcceptMulticast |
- AcceptMyPhys | AcceptAllPhys ), rtl->ioaddr + RxConfig );
- outl ( 0xffffffffUL, rtl->ioaddr + MAR0 + 0 );
- outl ( 0xffffffffUL, rtl->ioaddr + MAR0 + 4 );
- outl ( ( ( TX_DMA_BURST << 8 ) | ( TX_IPG << 24 ) ),
- rtl->ioaddr + TxConfig );
-
- return 0;
-}
-
-/**
- * Close NIC
- *
- * @v netdev Net device
- */
-static void rtl_close ( struct net_device *netdev ) {
- struct rtl8139_nic *rtl = netdev->priv;
-
- /* Reset the hardware to disable everything in one go */
- rtl_reset ( netdev );
-
- /* Free RX ring */
- free ( rtl->rx.ring );
- rtl->rx.ring = NULL;
-}
-
-/**
- * Transmit packet
- *
- * @v netdev Network device
- * @v iobuf I/O buffer
- * @ret rc Return status code
- */
-static int rtl_transmit ( struct net_device *netdev,
- struct io_buffer *iobuf ) {
- struct rtl8139_nic *rtl = netdev->priv;
-
- /* Check for space in TX ring */
- if ( rtl->tx.iobuf[rtl->tx.next] != NULL ) {
- DBGC ( rtl, "rtl8139 %p TX overflow\n", rtl );
- return -ENOBUFS;
- }
-
- /* Check for oversized packets */
- if ( iob_len ( iobuf ) >= TX_MAX_LEN ) {
- DBGC ( rtl, "rtl8139 %p TX too large (%zd bytes)\n",
- rtl, iob_len ( iobuf ) );
- return -ERANGE;
- }
-
- /* Pad and align packet */
- iob_pad ( iobuf, ETH_ZLEN );
-
- /* Add to TX ring */
- DBGC2 ( rtl, "rtl8139 %p TX id %d at %lx+%zx\n", rtl, rtl->tx.next,
- virt_to_bus ( iobuf->data ), iob_len ( iobuf ) );
- rtl->tx.iobuf[rtl->tx.next] = iobuf;
- outl ( virt_to_bus ( iobuf->data ),
- rtl->ioaddr + TxAddr0 + 4 * rtl->tx.next );
- outl ( ( ( ( TX_FIFO_THRESH & 0x7e0 ) << 11 ) | iob_len ( iobuf ) ),
- rtl->ioaddr + TxStatus0 + 4 * rtl->tx.next );
- rtl->tx.next = ( rtl->tx.next + 1 ) % TX_RING_SIZE;
-
- return 0;
-}
-
-/**
- * Poll for received packets
- *
- * @v netdev Network device
- */
-static void rtl_poll ( struct net_device *netdev ) {
- struct rtl8139_nic *rtl = netdev->priv;
- unsigned int status;
- unsigned int tsad;
- unsigned int rx_status;
- unsigned int rx_len;
- struct io_buffer *rx_iob;
- int wrapped_len;
- int i;
-
- /* Acknowledge interrupts */
- status = inw ( rtl->ioaddr + IntrStatus );
- if ( ! status )
- return;
- outw ( status, rtl->ioaddr + IntrStatus );
-
- /* Handle TX completions */
- tsad = inw ( rtl->ioaddr + TxSummary );
- for ( i = 0 ; i < TX_RING_SIZE ; i++ ) {
- if ( ( rtl->tx.iobuf[i] != NULL ) && ( tsad & ( 1 << i ) ) ) {
- DBGC2 ( rtl, "rtl8139 %p TX id %d complete\n",
- rtl, i );
- netdev_tx_complete ( netdev, rtl->tx.iobuf[i] );
- rtl->tx.iobuf[i] = NULL;
- }
- }
-
- /* Handle received packets */
- while ( ! ( inb ( rtl->ioaddr + ChipCmd ) & RxBufEmpty ) ) {
- rx_status = * ( ( uint16_t * )
- ( rtl->rx.ring + rtl->rx.offset ) );
- rx_len = * ( ( uint16_t * )
- ( rtl->rx.ring + rtl->rx.offset + 2 ) );
- if ( rx_status & RxOK ) {
- DBGC2 ( rtl, "rtl8139 %p RX packet at offset "
- "%x+%x\n", rtl, rtl->rx.offset, rx_len );
-
- rx_iob = alloc_iob ( rx_len );
- if ( ! rx_iob ) {
- netdev_rx_err ( netdev, NULL, -ENOMEM );
- /* Leave packet for next call to poll() */
- break;
- }
-
- wrapped_len = ( ( rtl->rx.offset + 4 + rx_len )
- - RX_BUF_LEN );
- if ( wrapped_len < 0 )
- wrapped_len = 0;
-
- memcpy ( iob_put ( rx_iob, rx_len - wrapped_len ),
- rtl->rx.ring + rtl->rx.offset + 4,
- rx_len - wrapped_len );
- memcpy ( iob_put ( rx_iob, wrapped_len ),
- rtl->rx.ring, wrapped_len );
- iob_unput ( rx_iob, 4 ); /* Strip CRC */
-
- netdev_rx ( netdev, rx_iob );
- } else {
- DBGC ( rtl, "rtl8139 %p RX bad packet (status %#04x "
- "len %d)\n", rtl, rx_status, rx_len );
- netdev_rx_err ( netdev, NULL, -EINVAL );
- }
- rtl->rx.offset = ( ( ( rtl->rx.offset + 4 + rx_len + 3 ) & ~3 )
- % RX_BUF_LEN );
- outw ( rtl->rx.offset - 16, rtl->ioaddr + RxBufPtr );
- }
-}
-
-/**
- * Enable/disable interrupts
- *
- * @v netdev Network device
- * @v enable Interrupts should be enabled
- */
-static void rtl_irq ( struct net_device *netdev, int enable ) {
- struct rtl8139_nic *rtl = netdev->priv;
-
- DBGC ( rtl, "rtl8139 %p interrupts %s\n",
- rtl, ( enable ? "enabled" : "disabled" ) );
- outw ( ( enable ? ( ROK | RER | TOK | TER ) : 0 ),
- rtl->ioaddr + IntrMask );
-}
-
-/** RTL8139 net device operations */
-static struct net_device_operations rtl_operations = {
- .open = rtl_open,
- .close = rtl_close,
- .transmit = rtl_transmit,
- .poll = rtl_poll,
- .irq = rtl_irq,
-};
-
-/**
- * Probe PCI device
- *
- * @v pci PCI device
- * @v id PCI ID
- * @ret rc Return status code
- */
-static int rtl_probe ( struct pci_device *pci ) {
- struct net_device *netdev;
- struct rtl8139_nic *rtl;
- int rc;
-
- /* Allocate net device */
- netdev = alloc_etherdev ( sizeof ( *rtl ) );
- if ( ! netdev )
- return -ENOMEM;
- netdev_init ( netdev, &rtl_operations );
- rtl = netdev->priv;
- pci_set_drvdata ( pci, netdev );
- netdev->dev = &pci->dev;
- memset ( rtl, 0, sizeof ( *rtl ) );
- rtl->ioaddr = pci->ioaddr;
-
- /* Fix up PCI device */
- adjust_pci_device ( pci );
-
- /* Reset the NIC, set up EEPROM access and read MAC address */
- rtl_reset ( netdev );
- rtl_init_eeprom ( netdev );
- nvs_read ( &rtl->eeprom.nvs, EE_MAC, netdev->hw_addr, ETH_ALEN );
-
- /* Register network device */
- if ( ( rc = register_netdev ( netdev ) ) != 0 )
- goto err_register_netdev;
-
- /* Mark as link up; we don't yet handle link state */
- netdev_link_up ( netdev );
-
- /* Register non-volatile storage */
- if ( rtl->nvo.nvs ) {
- if ( ( rc = register_nvo ( &rtl->nvo,
- netdev_settings ( netdev ) ) ) != 0)
- goto err_register_nvo;
- }
-
- return 0;
-
- err_register_nvo:
- unregister_netdev ( netdev );
- err_register_netdev:
- rtl_reset ( netdev );
- netdev_nullify ( netdev );
- netdev_put ( netdev );
- return rc;
-}
-
-/**
- * Remove PCI device
- *
- * @v pci PCI device
- */
-static void rtl_remove ( struct pci_device *pci ) {
- struct net_device *netdev = pci_get_drvdata ( pci );
- struct rtl8139_nic *rtl = netdev->priv;
-
- if ( rtl->nvo.nvs )
- unregister_nvo ( &rtl->nvo );
- unregister_netdev ( netdev );
- rtl_reset ( netdev );
- netdev_nullify ( netdev );
- netdev_put ( netdev );
-}
-
-static struct pci_device_id rtl8139_nics[] = {
-PCI_ROM(0x10ec, 0x8129, "rtl8129", "Realtek 8129", 0),
-PCI_ROM(0x10ec, 0x8139, "rtl8139", "Realtek 8139", 0),
-PCI_ROM(0x10ec, 0x8138, "rtl8139b", "Realtek 8139B", 0),
-PCI_ROM(0x1186, 0x1300, "dfe538", "DFE530TX+/DFE538TX", 0),
-PCI_ROM(0x1113, 0x1211, "smc1211-1", "SMC EZ10/100", 0),
-PCI_ROM(0x1112, 0x1211, "smc1211", "SMC EZ10/100", 0),
-PCI_ROM(0x1500, 0x1360, "delta8139", "Delta Electronics 8139", 0),
-PCI_ROM(0x4033, 0x1360, "addtron8139", "Addtron Technology 8139", 0),
-PCI_ROM(0x1186, 0x1340, "dfe690txd", "D-Link DFE690TXD", 0),
-PCI_ROM(0x13d1, 0xab06, "fe2000vx", "AboCom FE2000VX", 0),
-PCI_ROM(0x1259, 0xa117, "allied8139", "Allied Telesyn 8139", 0),
-PCI_ROM(0x14ea, 0xab06, "fnw3603tx", "Planex FNW-3603-TX", 0),
-PCI_ROM(0x14ea, 0xab07, "fnw3800tx", "Planex FNW-3800-TX", 0),
-PCI_ROM(0xffff, 0x8139, "clone-rtl8139", "Cloned 8139", 0),
-};
-
-struct pci_driver rtl8139_driver __pci_driver = {
- .ids = rtl8139_nics,
- .id_count = ( sizeof ( rtl8139_nics ) / sizeof ( rtl8139_nics[0] ) ),
- .probe = rtl_probe,
- .remove = rtl_remove,
-};