iPXE
velocity.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Adrian Jamróz <adrian.jamroz@gmail.com>
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  */
00019 
00020 FILE_LICENCE ( GPL2_OR_LATER );
00021 
00022 #include <stdint.h>
00023 #include <string.h>
00024 #include <unistd.h>
00025 #include <errno.h>
00026 #include <byteswap.h>
00027 #include <ipxe/netdevice.h>
00028 #include <ipxe/ethernet.h>
00029 #include <ipxe/if_ether.h>
00030 #include <ipxe/iobuf.h>
00031 #include <ipxe/malloc.h>
00032 #include <ipxe/pci.h>
00033 #include <ipxe/mii.h>
00034 #include "velocity.h"
00035 
00036 #define velocity_setbit(_reg, _mask)    writeb ( readb ( _reg ) | _mask, _reg )
00037 #define virt_to_le32bus(x)              ( cpu_to_le32 ( virt_to_bus ( x ) ) )
00038 
00039 /** @file
00040  *
00041  * VIA Velocity network driver
00042  *
00043  */
00044 
00045 /******************************************************************************
00046  *
00047  * MII interface
00048  *
00049  ******************************************************************************
00050  */
00051 
00052 /**
00053  * Stop MII auto-polling
00054  *
00055  * @v vlc       Velocity device
00056  * @ret rc      Return status code
00057  */
00058 static int velocity_autopoll_stop ( struct velocity_nic *vlc ) {
00059         int timeout = VELOCITY_TIMEOUT_US;
00060 
00061         /* Disable MII auto polling */
00062         writeb ( 0, vlc->regs + VELOCITY_MIICR );
00063 
00064         /* Wait for disabling to take effect */
00065         while ( timeout-- ) {
00066                 udelay ( 1 );
00067                 if ( readb ( vlc->regs + VELOCITY_MIISR ) &
00068                              VELOCITY_MIISR_IDLE )
00069                         return 0;
00070         }
00071 
00072         DBGC ( vlc, "MII autopoll stop timeout\n" );
00073         return -ETIMEDOUT;
00074 }
00075 
00076 /**
00077  * Start MII auto-polling
00078  *
00079  * @v vlc       Velocity device
00080  * @ret rc      Return status code
00081  */
00082 static int velocity_autopoll_start ( struct velocity_nic *vlc ) {
00083         int timeout = VELOCITY_TIMEOUT_US;
00084 
00085         /* Enable MII auto polling */
00086         writeb ( VELOCITY_MIICR_MAUTO, vlc->regs + VELOCITY_MIICR );
00087 
00088         /* Wait for enabling to take effect */
00089         while ( timeout-- ) {
00090                 udelay ( 1 );
00091                 if ( ( readb ( vlc->regs + VELOCITY_MIISR ) &
00092                        VELOCITY_MIISR_IDLE ) == 0 )
00093                         return 0;
00094         }
00095 
00096         DBGC ( vlc, "MII autopoll start timeout\n" );
00097         return -ETIMEDOUT;
00098 }
00099 
00100 /**
00101  * Read from MII register
00102  *
00103  * @v mdio              MII interface
00104  * @v phy               PHY address
00105  * @v reg               Register address
00106  * @ret value           Data read, or negative error
00107  */
00108 static int velocity_mii_read ( struct mii_interface *mdio,
00109                                unsigned int phy __unused, unsigned int reg ) {
00110         struct velocity_nic *vlc =
00111                 container_of ( mdio, struct velocity_nic, mdio );
00112         int timeout = VELOCITY_TIMEOUT_US;
00113         int result;
00114 
00115         DBGC2 ( vlc, "VELOCITY %p MII read reg %d\n", vlc, reg );
00116 
00117         /* Disable autopolling before we can access MII */
00118         velocity_autopoll_stop ( vlc );
00119 
00120         /* Send read command and address */
00121         writeb ( reg, vlc->regs + VELOCITY_MIIADDR );
00122         velocity_setbit ( vlc->regs + VELOCITY_MIICR, VELOCITY_MIICR_RCMD );
00123 
00124         /* Wait for read to complete */
00125         while ( timeout-- ) {
00126                 udelay ( 1 );
00127                 if ( ( readb ( vlc->regs + VELOCITY_MIICR ) &
00128                        VELOCITY_MIICR_RCMD ) == 0 ) {
00129                         result = readw ( vlc->regs + VELOCITY_MIIDATA );
00130                         velocity_autopoll_start ( vlc );
00131                         return result;
00132                 }
00133         }
00134 
00135         /* Restart autopolling */
00136         velocity_autopoll_start ( vlc );
00137 
00138         DBGC ( vlc, "MII read timeout\n" );
00139         return -ETIMEDOUT;
00140 }
00141 
00142 /**
00143  * Write to MII register
00144  *
00145  * @v mdio              MII interface
00146  * @v phy               PHY address
00147  * @v reg               Register address
00148  * @v data              Data to write
00149  * @ret rc              Return status code
00150  */
00151 static int velocity_mii_write ( struct mii_interface *mdio,
00152                                 unsigned int phy __unused, unsigned int reg,
00153                                 unsigned int data) {
00154         struct velocity_nic *vlc =
00155                 container_of ( mdio, struct velocity_nic, mdio );
00156         int timeout = VELOCITY_TIMEOUT_US;
00157 
00158         DBGC2 ( vlc, "VELOCITY %p MII write reg %d data 0x%04x\n",
00159                         vlc, reg, data );
00160 
00161         /* Disable autopolling before we can access MII */
00162         velocity_autopoll_stop ( vlc );
00163 
00164         /* Send write command, data and destination register */
00165         writeb ( reg, vlc->regs + VELOCITY_MIIADDR );
00166         writew ( data, vlc->regs + VELOCITY_MIIDATA );
00167         velocity_setbit ( vlc->regs + VELOCITY_MIICR, VELOCITY_MIICR_WCMD );
00168 
00169         /* Wait for write to complete */
00170         while ( timeout-- ) {
00171                 udelay ( 1 );
00172                 if ( ( readb ( vlc->regs + VELOCITY_MIICR ) &
00173                        VELOCITY_MIICR_WCMD ) == 0 ) {
00174                         velocity_autopoll_start ( vlc );
00175                         return 0;
00176                 }
00177         }
00178 
00179         /* Restart autopolling */
00180         velocity_autopoll_start ( vlc );
00181 
00182         DBGC ( vlc, "MII write timeout\n" );
00183         return -ETIMEDOUT;
00184 }
00185 
00186 /** Velocity MII operations */
00187 static struct mii_operations velocity_mii_operations = {
00188         .read = velocity_mii_read,
00189         .write = velocity_mii_write,
00190 };
00191 
00192 /**
00193  * Set Link speed 
00194  *
00195  * @v vlc       Velocity device
00196  */
00197 static void velocity_set_link ( struct velocity_nic *vlc ) {
00198         int tmp;
00199 
00200         /* Advertise 1000MBit */
00201         tmp = mii_read ( &vlc->mii, MII_CTRL1000 );
00202         tmp |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
00203         mii_write ( &vlc->mii, MII_CTRL1000, tmp );
00204 
00205         /* Enable GBit operation in MII Control Register */
00206         tmp = mii_read ( &vlc->mii, MII_BMCR );
00207         tmp |= BMCR_SPEED1000;
00208         mii_write ( &vlc->mii, MII_BMCR, tmp );
00209 }
00210 
00211 /******************************************************************************
00212  *
00213  * Device reset
00214  *
00215  ******************************************************************************
00216  */
00217 
00218 /**
00219  * Reload eeprom contents
00220  *
00221  * @v vlc               Velocity device
00222  */
00223 static int velocity_reload_eeprom ( struct velocity_nic *vlc ) {
00224         int timeout = VELOCITY_TIMEOUT_US;
00225 
00226         /* Initiate reload */
00227         velocity_setbit ( vlc->regs + VELOCITY_EECSR, VELOCITY_EECSR_RELOAD );
00228 
00229         /* Wait for reload to complete */
00230         while ( timeout-- ) {
00231                 udelay ( 1 );
00232                 if ( ( readb ( vlc->regs + VELOCITY_EECSR ) &
00233                        VELOCITY_EECSR_RELOAD ) == 0 )
00234                         return 0;
00235         }
00236 
00237         DBGC ( vlc, "VELOCITY %p EEPROM reload timeout\n", vlc );
00238         return -ETIMEDOUT;
00239 }
00240 
00241 /**
00242  * Reset hardware
00243  *
00244  * @v vlc               Velocity device
00245  * @ret rc              Return status code
00246  */
00247 static int velocity_reset ( struct velocity_nic *vlc ) {
00248         int timeout = VELOCITY_TIMEOUT_US;
00249         uint8_t tmp;
00250 
00251         DBGC ( vlc, "VELOCITY %p reset\n", vlc );
00252 
00253         /* clear sticky Power state bits */
00254         tmp = readb ( vlc->regs + VELOCITY_STICKY );
00255         tmp &= ~( VELOCITY_STICKY_DS0 | VELOCITY_STICKY_DS1 );
00256         writeb ( tmp, vlc->regs + VELOCITY_STICKY );
00257 
00258         /* clear PACPI, which might have been enabled by the EEPROM reload */
00259         tmp = readb ( vlc->regs + VELOCITY_CFGA );
00260         tmp &= ~VELOCITY_CFGA_PACPI;
00261         writeb ( tmp, vlc->regs + VELOCITY_CFGA );
00262 
00263         velocity_setbit ( vlc->regs + VELOCITY_CRS1, VELOCITY_CR1_SFRST );
00264 
00265         /* Wait for reset to complete */
00266         while ( timeout-- ) {
00267                 udelay ( 1 );
00268                 if ( ( readb ( vlc->regs + VELOCITY_CRS1 ) &
00269                        VELOCITY_CR1_SFRST ) == 0 )
00270                         return 0;
00271         }
00272 
00273         return -EINVAL;
00274 }
00275 
00276 /******************************************************************************
00277  *
00278  * Link state
00279  *
00280  ******************************************************************************
00281  */
00282 
00283 /**
00284  * Check link state
00285  *
00286  * @v netdev            Network device
00287  */
00288 static void velocity_check_link ( struct net_device *netdev ) {
00289         struct velocity_nic *vlc = netdev->priv;
00290 
00291         if ( readb ( vlc->regs + VELOCITY_PHYSTS0 ) & VELOCITY_PHYSTS0_LINK ) {
00292                 netdev_link_up ( netdev );
00293                 DBGC ( vlc, "VELOCITY %p link up\n", vlc );
00294         } else {
00295                 netdev_link_down ( netdev );
00296                 DBGC ( vlc, "VELOCITY %p link down\n", vlc );
00297         }
00298 
00299         /* The card disables auto-poll after a link change */
00300         velocity_autopoll_start ( vlc );
00301 }
00302 
00303 /******************************************************************************
00304  *
00305  * Network device interface
00306  *
00307  ******************************************************************************
00308  */
00309 
00310 /**
00311  * Allocate descriptor rings
00312  *
00313  * @v vlc       Velocity device
00314  * @ret rc      Return status code
00315  */
00316 static int velocity_alloc_rings ( struct velocity_nic *vlc ) {
00317         int rc = 0;
00318 
00319         /* Allocate RX descriptor ring */
00320         vlc->rx_prod = 0;
00321         vlc->rx_cons = 0;
00322         vlc->rx_commit = 0;
00323         vlc->rx_ring = malloc_dma ( VELOCITY_RXDESC_SIZE, VELOCITY_RING_ALIGN );
00324         if ( ! vlc->rx_ring )
00325                 return -ENOMEM;
00326 
00327         memset ( vlc->rx_ring, 0, VELOCITY_RXDESC_SIZE );
00328 
00329         DBGC2 ( vlc, "VELOCITY %p RX ring start address: %p(phys: %#08lx)\n",
00330                vlc, vlc->rx_ring, virt_to_bus ( vlc->rx_ring ) );
00331 
00332         /* Allocate TX descriptor ring */
00333         vlc->tx_prod = 0;
00334         vlc->tx_cons = 0;
00335         vlc->tx_ring = malloc_dma ( VELOCITY_TXDESC_SIZE, VELOCITY_RING_ALIGN );
00336         if ( ! vlc->tx_ring ) {
00337                 rc = -ENOMEM;
00338                 goto err_tx_alloc;
00339         }
00340 
00341         memset ( vlc->tx_ring, 0, VELOCITY_TXDESC_SIZE );
00342 
00343         /* Send RX ring to the card */
00344         writel ( virt_to_bus ( vlc->rx_ring ),
00345                  vlc->regs + VELOCITY_RXDESC_ADDR_LO );
00346         writew ( VELOCITY_RXDESC_NUM - 1, vlc->regs + VELOCITY_RXDESCNUM );
00347 
00348         /* Send TX ring to the card */
00349         writel ( virt_to_bus ( vlc->tx_ring ),
00350                  vlc->regs + VELOCITY_TXDESC_ADDR_LO0 );
00351         writew ( VELOCITY_TXDESC_NUM - 1, vlc->regs + VELOCITY_TXDESCNUM );
00352 
00353         DBGC2 ( vlc, "VELOCITY %p TX ring start address: %p(phys: %#08lx)\n",
00354                vlc, vlc->tx_ring, virt_to_bus ( vlc->tx_ring ) );
00355 
00356         return 0;
00357 
00358 err_tx_alloc:
00359         free_dma ( vlc->rx_ring, VELOCITY_RXDESC_SIZE );
00360         return rc;
00361 }
00362 
00363 /**
00364  * Refill receive descriptor ring
00365  *
00366  * @v vlc       Velocity device
00367  */
00368 static void velocity_refill_rx ( struct velocity_nic *vlc ) {
00369         struct velocity_rx_descriptor *desc;
00370         struct io_buffer *iobuf;
00371         int rx_idx, i = 0;
00372 
00373         /* Check for new packets */
00374         while ( ( vlc->rx_prod - vlc->rx_cons ) < VELOCITY_RXDESC_NUM ) {
00375                 iobuf = alloc_iob ( VELOCITY_RX_MAX_LEN );
00376 
00377                 /* Memory pressure: try again next poll */
00378                 if ( ! iobuf )
00379                         break;
00380 
00381                 rx_idx = ( vlc->rx_prod++ % VELOCITY_RXDESC_NUM );
00382                 desc = &vlc->rx_ring[rx_idx];
00383 
00384                 /* Set descrptor fields */
00385                 desc->des1 = 0;
00386                 desc->addr = virt_to_le32bus ( iobuf-> data );
00387                 desc->des2 = cpu_to_le32 (
00388                     VELOCITY_DES2_SIZE ( VELOCITY_RX_MAX_LEN - 1 ) |
00389                     VELOCITY_DES2_IC );
00390 
00391                 vlc->rx_buffs[rx_idx] = iobuf;
00392                 i++;
00393 
00394                 /* Return RX descriptors in blocks of 4 (hw requirement) */
00395                 if ( rx_idx % 4 == 3 ) {
00396                         int j;
00397                         for (j = 0; j < 4; j++) {
00398                                 desc = &vlc->rx_ring[rx_idx - j];
00399                                 desc->des0 = cpu_to_le32 ( VELOCITY_DES0_OWN );
00400                         }
00401                         vlc->rx_commit += 4;
00402                 }
00403         }
00404 
00405         wmb();
00406 
00407         if ( vlc->rx_commit ) {
00408                 writew ( vlc->rx_commit,
00409                          vlc->regs + VELOCITY_RXDESC_RESIDUECNT );
00410                 vlc->rx_commit = 0;
00411         }
00412 
00413         if ( i > 0 )
00414                 DBGC2 ( vlc, "VELOCITY %p refilled %d RX descriptors\n",
00415                         vlc, i );
00416 }
00417 
00418 /**
00419  * Open network device
00420  *
00421  * @v netdev            Network device
00422  * @ret rc              Return status code
00423  */
00424 static int velocity_open ( struct net_device *netdev ) {
00425         struct velocity_nic *vlc = netdev->priv;
00426         int rc;
00427 
00428         DBGC ( vlc, "VELOCITY %p open\n", vlc );
00429         DBGC ( vlc, "VELOCITY %p regs at: %p\n", vlc, vlc->regs );
00430 
00431         /* Allocate descriptor rings */
00432         if ( ( rc = velocity_alloc_rings ( vlc ) ) != 0 )
00433                 return rc;
00434 
00435         velocity_refill_rx ( vlc );
00436 
00437         /* Enable TX/RX queue */
00438         writew ( VELOCITY_TXQCSRS_RUN0, vlc->regs + VELOCITY_TXQCSRS );
00439         writew ( VELOCITY_RXQCSR_RUN | VELOCITY_RXQCSR_WAK,
00440                  vlc->regs + VELOCITY_RXQCSRS );
00441 
00442         /* Enable interrupts */
00443         writeb ( 0xff, vlc->regs + VELOCITY_IMR0 );
00444         writeb ( 0xff, vlc->regs + VELOCITY_IMR1 );
00445 
00446         /* Start MAC */
00447         writeb ( VELOCITY_CR0_STOP, vlc->regs + VELOCITY_CRC0 );
00448         writeb ( VELOCITY_CR1_DPOLL, vlc->regs + VELOCITY_CRC0 );
00449         writeb ( VELOCITY_CR0_START | VELOCITY_CR0_TXON | VELOCITY_CR0_RXON,
00450                  vlc->regs + VELOCITY_CRS0 );
00451 
00452         /* Receive all packets */
00453         writeb ( 0xff, vlc->regs + VELOCITY_RCR );
00454 
00455         /* Set initial link state */
00456         velocity_check_link ( netdev );
00457 
00458         velocity_autopoll_start ( vlc );
00459 
00460         DBGC2 ( vlc, "VELOCITY %p CR3 %02x\n",
00461                 vlc, readb ( vlc->regs + 0x0B ) );
00462 
00463         return 0;
00464 }
00465 
00466 /**
00467  * Close network device
00468  *
00469  * @v netdev            Network device
00470  */
00471 static void velocity_close ( struct net_device *netdev ) {
00472         struct velocity_nic *vlc = netdev->priv;
00473         int i;
00474 
00475         /* Stop NIC */
00476         writeb ( VELOCITY_CR0_TXON | VELOCITY_CR0_RXON,
00477                  vlc->regs + VELOCITY_CRC0 );
00478         writeb ( VELOCITY_CR0_STOP, vlc->regs + VELOCITY_CRS0 );
00479 
00480         /* Clear RX ring information */
00481         writel ( 0, vlc->regs + VELOCITY_RXDESC_ADDR_LO );
00482         writew ( 0, vlc->regs + VELOCITY_RXDESCNUM );
00483 
00484         /* Destroy RX ring */
00485         free_dma ( vlc->rx_ring, VELOCITY_RXDESC_SIZE );
00486         vlc->rx_ring = NULL;
00487         vlc->rx_prod = 0;
00488         vlc->rx_cons = 0;
00489 
00490         /* Discard receive buffers */
00491         for ( i = 0 ; i < VELOCITY_RXDESC_NUM ; i++ ) {
00492                 if ( vlc->rx_buffs[i] )
00493                         free_iob ( vlc->rx_buffs[i] );
00494                 vlc->rx_buffs[i] = NULL;
00495         }
00496 
00497         /* Clear TX ring information */
00498         writel ( 0, vlc->regs + VELOCITY_TXDESC_ADDR_LO0 );
00499         writew ( 0, vlc->regs + VELOCITY_TXDESCNUM );
00500 
00501         /* Destroy TX ring */
00502         free_dma ( vlc->tx_ring, VELOCITY_TXDESC_SIZE );
00503         vlc->tx_ring = NULL;
00504         vlc->tx_prod = 0;
00505         vlc->tx_cons = 0;
00506 }
00507 
00508 /**
00509  * Transmit packet
00510  *
00511  * @v netdev            Network device
00512  * @v iobuf             I/O buffer
00513  * @ret rc              Return status code
00514  */
00515 static int velocity_transmit ( struct net_device *netdev,
00516                                struct io_buffer *iobuf ) {
00517         struct velocity_nic *vlc = netdev->priv;
00518         struct velocity_tx_descriptor *desc;
00519         unsigned int tx_idx;
00520 
00521         /* Pad packet to minimum length */
00522         iob_pad ( iobuf, ETH_ZLEN );
00523 
00524         tx_idx = ( vlc->tx_prod++ % VELOCITY_TXDESC_NUM );
00525         desc = &vlc->tx_ring[tx_idx];
00526 
00527         /* Set packet size and transfer ownership to NIC */
00528         desc->des0 = cpu_to_le32 ( VELOCITY_DES0_OWN |
00529                                    VELOCITY_DES2_SIZE ( iob_len ( iobuf ) ) );
00530         /* Data in first desc fragment, only desc for packet, generate INT */
00531         desc->des1 = cpu_to_le32 ( VELOCITY_DES1_FRAG ( 1 ) |
00532                                    VELOCITY_DES1_TCPLS |
00533                                    VELOCITY_DES1_INTR );
00534 
00535         desc->frags[0].addr = virt_to_le32bus ( iobuf->data );
00536         desc->frags[0].des2 = cpu_to_le32 (
00537                               VELOCITY_DES2_SIZE ( iob_len ( iobuf ) ) );
00538 
00539         wmb();
00540 
00541         /* Initiate TX */
00542         velocity_setbit ( vlc->regs + VELOCITY_TXQCSRS, VELOCITY_TXQCSRS_WAK0 );
00543 
00544         DBGC2 ( vlc, "VELOCITY %p tx_prod=%d desc=%p iobuf=%p len=%zd\n",
00545                 vlc, tx_idx, desc, iobuf->data, iob_len ( iobuf ) );
00546 
00547         return 0;
00548 }
00549 
00550 /**
00551  * Poll for received packets.
00552  *
00553  * @v vlc       Velocity device
00554  */
00555 static void velocity_poll_rx ( struct velocity_nic *vlc ) {
00556         struct velocity_rx_descriptor *desc;
00557         struct io_buffer *iobuf;
00558         int rx_idx;
00559         size_t len;
00560         uint32_t des0;
00561 
00562         /* Check for packets */
00563         while ( vlc->rx_cons != vlc->rx_prod ) {
00564                 rx_idx = ( vlc->rx_cons % VELOCITY_RXDESC_NUM );
00565                 desc = &vlc->rx_ring[rx_idx];
00566 
00567                 des0 = cpu_to_le32 ( desc->des0 );
00568 
00569                 /* Return if descriptor still in use */
00570                 if ( des0 & VELOCITY_DES0_OWN )
00571                         return;
00572 
00573                 iobuf = vlc->rx_buffs[rx_idx];
00574 
00575                 /* Get length, strip CRC */
00576                 len = VELOCITY_DES0_RMBC ( des0 ) - 4;
00577                 iob_put ( iobuf, len );
00578 
00579                 DBGC2 ( vlc, "VELOCITY %p got packet on idx=%d (prod=%d), len %zd\n",
00580                     vlc, rx_idx, vlc->rx_prod % VELOCITY_RXDESC_NUM, len );
00581 
00582                 if ( des0 & VELOCITY_DES0_RX_ERR ) {
00583                         /* Report receive error */
00584                         netdev_rx_err ( vlc->netdev, iobuf, -EINVAL );
00585                         DBGC ( vlc, "VELOCITY %p receive error, status: %02x\n",
00586                                vlc, des0 );
00587                 } else if ( des0 & VELOCITY_DES0_RXOK ) {
00588                         /* Report receive success */
00589                         netdev_rx( vlc->netdev, iobuf );
00590                 } else {
00591                         /* Card indicated neither success nor failure
00592                          * Technically this shouldn't happen, but we saw it
00593                          * in debugging once. */
00594                         DBGC ( vlc, "VELOCITY %p RX neither ERR nor OK: %04x\n",
00595                                vlc, des0 );
00596                         DBGC ( vlc, "packet len: %zd\n", len );
00597                         DBGC_HD ( vlc, iobuf->data, 64 );
00598 
00599                         /* we don't know what it is, treat is as an error */
00600                         netdev_rx_err ( vlc->netdev, iobuf, -EINVAL );
00601                 }
00602 
00603                 vlc->rx_cons++;
00604         }
00605 }
00606 
00607 /**
00608  * Poll for completed packets.
00609  *
00610  * @v vlc       Velocity device
00611  */
00612 static void velocity_poll_tx ( struct velocity_nic *vlc ) {
00613         struct velocity_tx_descriptor *desc;
00614         int tx_idx;
00615 
00616         /* Check for packets */
00617         while ( vlc->tx_cons != vlc->tx_prod ) {
00618                 tx_idx = ( vlc->tx_cons % VELOCITY_TXDESC_NUM );
00619                 desc = &vlc->tx_ring[tx_idx];
00620 
00621                 /* Return if descriptor still in use */
00622                 if ( le32_to_cpu ( desc->des0 ) & VELOCITY_DES0_OWN )
00623                         return;
00624 
00625                 /* Report errors */
00626                 if ( le32_to_cpu ( desc->des0 ) & VELOCITY_DES0_TERR ) {
00627                         netdev_tx_complete_next_err ( vlc->netdev, -EINVAL );
00628                         return;
00629                 }
00630 
00631                 netdev_tx_complete_next ( vlc->netdev );
00632 
00633                 DBGC2 ( vlc, "VELOCITY %p poll_tx cons=%d prod=%d tsr=%04x\n",
00634                         vlc, tx_idx, vlc->tx_prod % VELOCITY_TXDESC_NUM,
00635                         ( desc->des0 & 0xffff ) );
00636                 vlc->tx_cons++;
00637         }
00638 }
00639 
00640 /**
00641  * Poll for completed and received packets
00642  *
00643  * @v netdev            Network device
00644  */
00645 static void velocity_poll ( struct net_device *netdev ) {
00646         struct velocity_nic *vlc = netdev->priv;
00647         uint8_t isr1;
00648 
00649         isr1 = readb ( vlc->regs + VELOCITY_ISR1 );
00650 
00651         /* ACK interrupts */
00652         writew ( 0xFFFF, vlc->regs + VELOCITY_ISR0 );
00653 
00654         /* Check for competed packets */
00655         velocity_poll_rx ( vlc );
00656         velocity_poll_tx ( vlc );
00657 
00658         if ( isr1 & VELOCITY_ISR1_SRCI ) {
00659                 /* Update linkstate */
00660                 DBGC2 ( vlc, "VELOCITY %p link status interrupt\n", vlc );
00661                 velocity_check_link ( netdev );
00662         }
00663 
00664         velocity_refill_rx ( vlc );
00665 
00666         /* deal with potential RX stall caused by RX ring underrun */
00667         writew ( VELOCITY_RXQCSR_RUN | VELOCITY_RXQCSR_WAK,
00668                  vlc->regs + VELOCITY_RXQCSRS );
00669 }
00670 
00671 /**
00672  * Enable or disable interrupts
00673  *
00674  * @v netdev            Network device
00675  * @v enable            Interrupts should be enabled
00676  */
00677 static void velocity_irq ( struct net_device *netdev, int enable ) {
00678         struct velocity_nic *vlc = netdev->priv;
00679 
00680         DBGC ( vlc, "VELOCITY %p interrupts %s\n", vlc,
00681             enable ? "enable" : "disable" );
00682 
00683         if (enable) {
00684                 /* Enable interrupts */
00685                 writeb ( VELOCITY_CR3_GINTMSK1, vlc->regs + VELOCITY_CRS3 );
00686         } else {
00687                 /* Disable interrupts */
00688                 writeb ( VELOCITY_CR3_GINTMSK1, vlc->regs + VELOCITY_CRC3 );
00689         }
00690 }
00691 
00692 /** Velocity network device operations */
00693 static struct net_device_operations velocity_operations = {
00694         .open           = velocity_open,
00695         .close          = velocity_close,
00696         .transmit       = velocity_transmit,
00697         .poll           = velocity_poll,
00698         .irq            = velocity_irq,
00699 };
00700 
00701 /******************************************************************************
00702  *
00703  * PCI interface
00704  *
00705  ******************************************************************************
00706  */
00707 
00708 /**
00709  * Probe PCI device
00710  *
00711  * @v pci               PCI device
00712  * @ret rc              Return status code
00713  */
00714 static int velocity_probe ( struct pci_device *pci ) {
00715         struct net_device *netdev;
00716         struct velocity_nic *vlc;
00717         int rc;
00718 
00719         /* Allocate and initialise net device */
00720         netdev = alloc_etherdev ( sizeof ( *vlc ) );
00721         if ( ! netdev ) {
00722                 rc = -ENOMEM;
00723                 goto err_alloc;
00724         }
00725         netdev_init ( netdev, &velocity_operations );
00726         vlc = netdev->priv;
00727         pci_set_drvdata ( pci, netdev );
00728         netdev->dev = &pci->dev;
00729 
00730         /* Fix up PCI device */
00731         adjust_pci_device ( pci );
00732 
00733         /* Map registers */
00734         vlc->regs = ioremap ( pci->membase, VELOCITY_BAR_SIZE );
00735         vlc->netdev = netdev;
00736 
00737         /* Reset the NIC */
00738         if ( ( rc = velocity_reset ( vlc ) ) != 0 )
00739                 goto err_reset;
00740 
00741         /* Reload EEPROM */
00742         if ( ( rc = velocity_reload_eeprom ( vlc ) ) != 0 )
00743                 goto err_reset;
00744 
00745         /* Get MAC address */
00746         netdev->hw_addr[0] = readb ( vlc->regs + VELOCITY_MAC0 );
00747         netdev->hw_addr[1] = readb ( vlc->regs + VELOCITY_MAC1 );
00748         netdev->hw_addr[2] = readb ( vlc->regs + VELOCITY_MAC2 );
00749         netdev->hw_addr[3] = readb ( vlc->regs + VELOCITY_MAC3 );
00750         netdev->hw_addr[4] = readb ( vlc->regs + VELOCITY_MAC4 );
00751         netdev->hw_addr[5] = readb ( vlc->regs + VELOCITY_MAC5 );
00752 
00753         /* Initialise and reset MII interface */
00754         mdio_init ( &vlc->mdio, &velocity_mii_operations );
00755         mii_init ( &vlc->mii, &vlc->mdio, 0 );
00756         if ( ( rc = mii_reset ( &vlc->mii ) ) != 0 ) {
00757                 DBGC ( vlc, "VELOCITY %p could not reset MII: %s\n",
00758                        vlc, strerror ( rc ) );
00759                 goto err_mii_reset;
00760         }
00761 
00762         /* Enable proper link advertising */
00763         velocity_set_link ( vlc );
00764 
00765         /* Register network device */
00766         if ( ( rc = register_netdev ( netdev ) ) != 0 )
00767                 goto err_register_netdev;
00768 
00769         return 0;
00770 
00771  err_register_netdev:
00772  err_mii_reset:
00773         velocity_reset ( vlc );
00774  err_reset:
00775         netdev_nullify ( netdev );
00776         netdev_put ( netdev );
00777  err_alloc:
00778         return rc;
00779 }
00780 
00781 /**
00782  * Remove PCI device
00783  *
00784  * @v pci               PCI device
00785  */
00786 static void velocity_remove ( struct pci_device *pci ) {
00787         struct net_device *netdev = pci_get_drvdata ( pci );
00788         struct velocity_nic *vlc = netdev->priv;
00789 
00790         /* Unregister network device */
00791         unregister_netdev ( netdev );
00792 
00793         /* Reset card */
00794         velocity_reset ( vlc );
00795 
00796         /* Free network device */
00797         netdev_nullify ( netdev );
00798         netdev_put ( netdev );
00799 }
00800 
00801 /** Velocity PCI device IDs */
00802 static struct pci_device_id velocity_nics[] = {
00803         PCI_ROM ( 0x1106, 0x3119, "vt6122",     "VIA Velocity", 0 ),
00804 };
00805 
00806 /** Velocity PCI driver */
00807 struct pci_driver velocity_driver __pci_driver = {
00808         .ids = velocity_nics,
00809         .id_count = ( sizeof ( velocity_nics ) / sizeof ( velocity_nics[0] ) ),
00810         .probe = velocity_probe,
00811         .remove = velocity_remove,
00812 };