ax88796.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2003-2005 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  * --
00033  * Initially taken from Marek Hummel's port of the Realtek driver.
00034  *
00035  * Revision 1.0  2004/07/20 17:29:08  MarekH
00036  */
00037 
00038 /*
00039  * $Log: ax88796.c,v $
00040  * Revision 1.7  2008/08/11 06:59:07  haraldkipp
00041  * BSD types replaced by stdint types (feature request #1282721).
00042  *
00043  * Revision 1.6  2007/05/02 11:22:51  haraldkipp
00044  * Added multicast table entry.
00045  *
00046  * Revision 1.5  2006/06/28 17:10:15  haraldkipp
00047  * Include more general header file for ARM.
00048  *
00049  * Revision 1.4  2006/03/02 19:48:19  haraldkipp
00050  * Replaced inline assembly nops with their portable counterparts.
00051  *
00052  * Revision 1.3  2006/01/23 17:27:47  haraldkipp
00053  * Previous hack to fix missing network configuration routines disabled
00054  * non-volatile memory access for ARM.
00055  *
00056  * Revision 1.2  2005/10/22 08:55:47  haraldkipp
00057  * CPU specific headers moved to subdirectories for the CPU family.
00058  *
00059  * Revision 1.1  2005/07/26 18:02:26  haraldkipp
00060  * Moved from dev.
00061  *
00062  * Revision 1.1  2005/04/05 17:47:48  haraldkipp
00063  * Initial check in. For ARM only.
00064  *
00065  */
00066 
00067 #include <arch/arm.h>
00068 
00069 #include <string.h>
00070 //#include <stdio.h>
00071 
00072 #include <sys/atom.h>
00073 #include <sys/heap.h>
00074 #include <sys/thread.h>
00075 #include <sys/event.h>
00076 #include <sys/timer.h>
00077 #include <sys/confnet.h>
00078 
00079 #include <dev/irqreg.h>
00080 #include <dev/ax88796.h>
00081 #include "reg_ax88796.h"
00082 
00083 #define ASIX_RESET_PIN 10
00084 
00085 static NICINFO dcb_eth0;
00086 
00091 
00092 
00098 static IFNET ifn_eth0 = {
00099     IFT_ETHER,                  
00100     {0, 0, 0, 0, 0, 0},         
00101     0,                          
00102     0,                          
00103     0,                          
00104     ETHERMTU,                   
00105     0,                          
00106     0,                          
00107     0,                          
00108     NutEtherInput,              
00109     AsixOutput,                 
00110     NutEtherOutput              
00111 };
00112 
00122 NUTDEVICE devAx88796 = {
00123     0,                          /* Pointer to next device. */
00124     {'e', 't', 'h', '0', 0, 0, 0, 0, 0},        /* Unique device name. */
00125     IFTYP_NET,                  /* Type of device. */
00126     0,                          /* Base address. */
00127     0,                          /* First interrupt number. */
00128     &ifn_eth0,                  /* Interface control block. */
00129     &dcb_eth0,                  /* Driver control block. */
00130     AsixInit,                   /* Driver initialization routine. */
00131     0,                          /* Driver specific control function. */
00132     0,                          /* Read from device. */
00133     0,                          /* Write to device. */
00134     0,                          /* Open a device or file. */
00135     0,                          /* Close a device or file. */
00136     0                           /* Request file size. */
00137 };
00138 
00142 struct nic_pkt_header {
00143     uint8_t ph_status;           
00144     uint8_t ph_nextpg;           
00145     uint16_t ph_size;            
00146 };
00147 
00148 /*
00149  * This delay has been added by Bengt Florin and is used to minimize 
00150  * the effect of the IORDY line during reads. Bengt contributed a
00151  * more versatile loop, which unfortunately wasn't portable to the
00152  * ImageCraft compiler.
00153  *
00154  * Both versions depend on the CPU clock and had been tested with
00155  * 14.7456 MHz.
00156  */
00157 void Delay16Cycles(void)
00158 {
00159     _NOP();
00160     _NOP();
00161     _NOP();
00162     _NOP();
00163     _NOP();
00164     _NOP();
00165     _NOP();
00166     _NOP();
00167     _NOP();
00168     _NOP();
00169     _NOP();
00170     _NOP();
00171     _NOP();
00172     _NOP();
00173     _NOP();
00174     _NOP();
00175 }
00176 
00177 //==============================================================================
00183 static uint16_t MIIPutGet(uint16_t data, uint8_t bitCount)
00184 {
00185     uint16_t rc = 0;
00186     uint16_t mask;
00187     uint8_t i;
00188 
00189     mask = 1 << (bitCount - 1);
00190 
00191     for (i = 0; i < bitCount; i++) {
00192 
00193         /* send data to MII */
00194         if (data & mask) {
00195             Asix_Write(MII_EEP, (Asix_Read(MII_EEP) | MII_EEP_MDO));
00196         } else {
00197             Asix_Write(MII_EEP, (Asix_Read(MII_EEP) & ~(MII_EEP_MDO)));
00198         }
00199         /* clock and data recieve from MII */
00200         Asix_Write(MII_EEP, (Asix_Read(MII_EEP) | MII_EEP_MDC));        //clock up
00201         Delay16Cycles();
00202 
00203         data <<= 1;
00204         rc <<= 1;
00205         rc |= (Asix_Read(MII_EEP) & MII_EEP_MDI) != 0;  //read MII
00206         Asix_Write(MII_EEP, (Asix_Read(MII_EEP) & ~(MII_EEP_MDC)));     //clock down
00207     }
00208     return rc;
00209 }
00210 
00211 //==============================================================================
00218 uint16_t NicPhyRead(uint8_t reg)
00219 {
00220     uint16_t rc = 0;
00221 
00222     /* Select Bank 0. */
00223     Asix_Write(CR, (Asix_Read(CR) & ~(CR_PS0)));
00224 
00225     /* Preamble. Send idle pattern, 32 '1's to synchronize MII. */
00226     MIIPutGet(0xFFFF, 16);
00227     MIIPutGet(0xFFFF, 16);
00228 
00229     /* Start(01), Read(10), PhyAdr(10000) */
00230     MIIPutGet(0xD0, 9);
00231 
00232     /* Reg address<5:0> */
00233     MIIPutGet(reg, 5);
00234 
00235     /* TA(Z0), no support high-Z */
00236     MIIPutGet(0x0, 1);
00237 
00238     /* Read data from internel PHY */
00239     rc = MIIPutGet(0, 16);
00240 
00241     return rc;
00242 }
00243 
00244 //==============================================================================
00253 void NicPhyWrite(uint8_t reg, uint16_t val)
00254 {
00255 
00256     /* Select Bank 0. */
00257     Asix_Write(CR, (Asix_Read(CR) & ~(CR_PS0)));
00258 
00259     /* Preamble. Send idle pattern, 32 '1's to synchronize MII. */
00260     MIIPutGet(0xFFFF, 16);
00261     MIIPutGet(0xFFFF, 16);
00262 
00263     /* Start(01), Write(01), PhyAdr(10000) */
00264     MIIPutGet(0xB0, 9);
00265 
00266     /* Reg address<5:0> */
00267     MIIPutGet(reg, 5);
00268 
00269     /* TA(01) */
00270     MIIPutGet(0x02, 2);
00271 
00272     /* Write data to internel PHY */
00273     MIIPutGet(val, 16);
00274 }
00275 
00276 //==============================================================================
00277 
00281 static void NicCompleteDma(void)
00282 {
00283     uint8_t i;
00284 
00285     /* Check that we have a DMA complete flag. */
00286     do {
00287         i = Asix_Read(PG0_ISR);
00288     } while ((i & ISR_RDC) == 0);
00289 
00290     /* Complete remote dma. */
00291     Asix_Write(CR, CR_START | CR_RD2);
00292 
00293     /* Reset remote dma complete flag. */
00294     Asix_Write(PG0_ISR, ISR_RDC);
00295 
00296     /* Wait for intterupt flag. */
00297     Delay16Cycles();
00298 }
00299 
00300 //==============================================================================
00306 static int NicReset(void)
00307 {
00308     int tmp;
00309     //uint16_t test;
00310 
00311     //printf("NicReset()\n");
00312     outr(PIO_PER, _BV(ASIX_RESET_PIN));  /* Set PIO Enable Register */
00313     outr(PIO_OER, _BV(ASIX_RESET_PIN));  /* Set PIO Status Register */
00314     outr(PIO_SODR, _BV(ASIX_RESET_PIN)); /* ASIX_RESET_PIN = 1 */
00315     NutDelay(100);
00316     outr(PIO_CODR, _BV(ASIX_RESET_PIN)); /* ASIX_RESET_PIN = 0 */
00317 
00318     /* wait for PHY to come out of reset. */
00319     tmp = 10;
00320     while (1) {
00321         NutDelay(255);
00322         if (!(Asix_Read(TR) & TR_RST_B))
00323             break;
00324         if (tmp-- == 0)
00325             return -1;
00326     }
00327 
00328     //test = NicPhyRead(PHY_MR2);
00329     //printf("PHY_MR2 = 0x%.04x\n\r", test);
00330     //test = NicPhyRead(PHY_MR3);
00331     //printf("PHY_MR3 = 0x%.04x\n\r", test);
00332 
00333 
00334 /* Following actions will fix the problem of long auto negotiation. */
00335 //      NicPhyWrite(0x00,0x0800);
00336 //      NutSleep(2500);
00337 //      NicPhyWrite(0x00,0x1200); /* set speed to auto */
00338     return 0;
00339 }
00340 
00341 //==============================================================================
00342 /*
00343  * Fires up the network interface. NIC interrupts
00344  * should have been disabled when calling this
00345  * function.
00346  *
00347  * \param mac Six byte unique MAC address.
00348  */
00349 static int NicStart(CONST uint8_t * mac)
00350 {
00351     uint8_t i;
00352 
00353     //printf("NicStart()\n");
00354     if (NicReset())
00355         return -1;
00356 
00357     /* Stop the NIC, abort DMA, page 0. */
00358     Asix_Write(CR, (CR_RD2 | CR_STOP));
00359 
00360     /* Selects word-wide DMA transfers. */
00361     Asix_Write(PG0_DCR, DCR_WTS);
00362 
00363     /* Load data byte count for remote DMA. */
00364     Asix_Write(PG0_RBCR0, 0x00);
00365     Asix_Write(PG0_RBCR1, 0x00);
00366 
00367     /* Temporarily set receiver to monitor mode. */
00368     Asix_Write(PG0_RCR, RCR_MON);
00369 
00370     /* Transmitter set to internal loopback mode. */
00371     Asix_Write(PG0_TCR, TCR_LB0);
00372 
00373     /* Initialize Recieve Buffer Ring. */
00374     Asix_Write(PG0_BNRY, RXSTART_INIT);
00375     Asix_Write(PG0_PSTART, RXSTART_INIT);
00376     Asix_Write(PG0_PSTOP, RXSTOP_INIT);
00377 
00378     /* Clear interrupt status. */
00379     Asix_Write(PG0_ISR, 0xFF);
00380 
00381     /* Initialize interrupt mask. */
00382     Asix_Write(PG0_IMR, IMR_PRXE | IMR_PTXE | IMR_RXEE | IMR_TXEE | IMR_OVWE);
00383 
00384     /* Stop the NIC, abort DMA, page 1. */
00385     Asix_Write(CR, (CR_PS0 | CR_RD2 | CR_STOP));
00386     Delay16Cycles();
00387 
00388     /* Set Physical address - MAC. */
00389     for (i = 0; i < 6; i++) {
00390         Asix_Write(PG1_PAR0 + i, mac[i]);
00391     }
00392 
00393     /* Set Multicast address. */
00394     for (i = 0; i < 8; i++) {
00395         Asix_Write(PG1_MAR0 + i, 0x00);
00396     }
00397 
00398     /* Set Current pointer point. */
00399     Asix_Write(PG1_CPR, RXSTART_INIT + 1);
00400 
00401     /* Start the NIC, Abort DMA, page 0. */
00402     Asix_Write(CR, (CR_RD2 | CR_START));        // stop the NIC, abort DMA, page 0   
00403     Delay16Cycles();
00404 
00405     /* Select media interfac. */
00406     Asix_Write(GPOC, 0x10);
00407 
00408     /* Check PHY speed setting 100base = full duplex, 10base > half duplex. */
00409     if (Asix_Read(GPI) & 0x04) {
00410         //printf("Full duplex\n");
00411         Asix_Write(PG0_TCR, TCR_FDU);
00412     }
00413     else {
00414         //printf("Half duplex\n");
00415         Asix_Write(PG0_TCR, 0);
00416     }
00417 
00418     /* Enable receiver and set accept broadcast. */
00419     //Asix_Write(PG0_RCR, (RCR_INTT | RCR_AB));
00420     Asix_Write(PG0_RCR, RCR_AB);
00421 
00422     return 0;
00423 }
00424 
00425 //==============================================================================
00426 /*
00427  * Write data block to the NIC.
00428  */
00429 static void NicWrite(uint8_t * buf, uint16_t len)
00430 {
00431     register uint16_t *wp = (uint16_t *) buf;
00432 
00433     if (len & 1)
00434         len++;
00435     len >>= 1;
00436     //printf("Write(%u): ", len);
00437     while (len--) {
00438         //printf("%04X ", *wp);
00439         Asix_WriteWord(DATAPORT, *wp);
00440         wp++;
00441     }
00442     //putchar('\n');
00443 }
00444 
00445 //==============================================================================
00446 /*
00447  * Read data block from the NIC.
00448  */
00449 static void NicRead(uint8_t * buf, uint16_t len)
00450 {
00451     register uint16_t *wp = (uint16_t *) buf;
00452 
00453     if (len & 1)
00454         len++;
00455     len >>= 1;
00456     //printf("Read(%u): ", len);
00457     while (len--) {
00458         *wp = Asix_ReadWord(DATAPORT);
00459         //printf("%04X ", *wp);
00460         wp++;
00461     }
00462     //putchar('\n');
00463 }
00464 
00465 //==============================================================================
00476 static NETBUF *NicGetPacket(void)
00477 {
00478     NETBUF *nb = 0;
00479     struct nic_pkt_header hdr;
00480     uint16_t count;
00481     uint8_t nextpg;
00482     uint8_t bnry;
00483     uint8_t curr;
00484     uint8_t drop = 0;
00485 
00486     /* we don't want to be interrupted by NIC owerflow */
00487     NutEnterCritical();
00488 
00489     /*
00490      * Get the current page pointer. It points to the page where the NIC 
00491      * will start saving the next incoming packet.
00492      */
00493     curr = Asix_Read(PG0_CPR);
00494     //printf("curr=%02X\n", curr);
00495 
00496     /*
00497      * Get the pointer to the last page we read from. The following page
00498      * is the one where we start reading. If it's equal to the current
00499      * page pointer, then there's nothing to read. In this case we return
00500      * a null pointer.
00501      */
00502     if ((bnry = Asix_Read(PG0_BNRY) + 1) >= RXSTOP_INIT) {
00503         //printf("bnry=%02X?\n", bnry);
00504         bnry = RXSTART_INIT;
00505     }
00506     if (bnry == curr) {
00507         //printf("bnry=%02X\n", bnry);
00508         NutJumpOutCritical();
00509         return 0;
00510     }
00511     /*
00512      * Read the NIC specific packet header (4 bytes).
00513      */
00514     Asix_Write(PG0_RBCR0, sizeof(struct nic_pkt_header));
00515     Asix_Write(PG0_RBCR1, 0);
00516     Asix_Write(PG0_RSAR0, 0);
00517     Asix_Write(PG0_RSAR1, bnry);
00518     Asix_Write(CR, CR_START | CR_RD0);
00519     Delay16Cycles();
00520 
00521     NicRead((uint8_t *) & hdr, sizeof(struct nic_pkt_header));
00522     NicCompleteDma();
00523     //printf("[S=%02X N=%02X L=%u]", hdr.ph_status, hdr.ph_nextpg, hdr.ph_size);
00524 
00525     /*
00526      *  Check packet length. Silently discard packets of illegal size.
00527      */
00528     if (hdr.ph_size < 60 + sizeof(struct nic_pkt_header) || hdr.ph_size > 1514 + sizeof(struct nic_pkt_header)) {
00529         //printf("Drop\n");
00530         drop = 1;
00531     }
00532 
00533     /*
00534      * Calculate the page of the next packet. If it differs from the
00535      * pointer in the packet header, we discard the whole buffer
00536      * and return a null pointer.
00537      */
00538 
00539 
00540 //      nextpg = hdr.ph_nextpg;
00541 //  bnry = hdr.ph_nextpg - 1;
00542 //      if(bnry < RXSTART_INIT) bnry = RXSTOP_INIT - 1; 
00543 
00544 //  printf("hdr.ph_size = %02u\n\r",hdr.ph_size);
00545 //  printf("hdr.ph_nextpg = %02x\n\r",hdr.ph_nextpg);
00546 
00547     nextpg = bnry + (hdr.ph_size >> 8) + ((hdr.ph_size & 0xFF) != 0);
00548     //printf("[nextpg = %02x ->", nextpg);
00549 
00550     if (nextpg >= RXSTOP_INIT) {
00551         nextpg -= RXSTOP_INIT;
00552         nextpg += RXSTART_INIT;
00553     }
00554     if (nextpg != hdr.ph_nextpg) {
00555         uint8_t nextpg1 = nextpg + 1;
00556         if (nextpg1 >= RXSTOP_INIT) {
00557             nextpg1 -= RXSTOP_INIT;
00558             nextpg1 += RXSTART_INIT;
00559         }
00560         //HK if (nextpg1 != hdr.ph_nextpg) {
00561         //HK    return (NETBUF *) 0xFFFF;
00562         //HK }
00563         nextpg = nextpg1;
00564     }
00565     //printf("%02x]", nextpg);
00566 
00567 
00568     /*
00569      * Check packet status. It should have set bit 0, but
00570      * even without this bit packets seem to be OK.
00571      */
00572     //printf("Drop=%u Status=%02X\n", drop, hdr.ph_status & 0x0E);
00573     if (!drop && ((hdr.ph_status & 0x0E) == 0)) {
00574 
00575         /* Allocate a NETBUF. */
00576         count = hdr.ph_size - sizeof(struct nic_pkt_header);
00577         //printf("Count=%u\n", count);
00578         if ((nb = NutNetBufAlloc(0, NBAF_DATALINK, count))) {
00579             /*
00580              * Set remote dma byte count and
00581              * start address. Don't read the
00582              * header again.
00583              */
00584             Asix_Write(PG0_RBCR0, count);
00585             Asix_Write(PG0_RBCR1, count >> 8);
00586             Asix_Write(PG0_RSAR0, sizeof(struct nic_pkt_header));
00587             Asix_Write(PG0_RSAR1, bnry);
00588 
00589             /*
00590              * Perform the read.
00591              */
00592             Asix_Write(CR, CR_START | CR_RD0);
00593             Delay16Cycles();
00594             NicRead(nb->nb_dl.vp, count);
00595             NicCompleteDma();
00596         }
00597     }
00598 
00599     /*
00600      * Set boundary register to the last page we read.
00601      * This also drops packets with errors
00602      */
00603     if (--nextpg < RXSTART_INIT)
00604         nextpg = RXSTOP_INIT - 1;
00605     Asix_Write(PG0_BNRY, nextpg);
00606     NutExitCritical();
00607     return nb;
00608 
00609 }
00610 
00611 //==============================================================================
00612 /*
00613  * \brief Handle NIC overflows.
00614  *
00615  * When a receiver buffer overflow occurs, the NIC will defer any subsequent 
00616  * action until properly restarted.
00617  *
00618  * This routine is called within interrupt context, which introduces a big
00619  * problem. It waits for the last transmission to finish, which may take
00620  * several milliseconds. Since Nut/OS 3.5, we do not support nested interrupts
00621  * on AVR systems anymore. So this routine may now increase interrupt
00622  * latency in an unacceptable way. The solution might be to handle overflows
00623  * in the receiver thread.
00624  *
00625  * In any case, this routines needs a major redesign. But it has been
00626  * tested in its current form to gracefully withstand ping floods. Thanks
00627  * to Bengt Florin for contributing his code, which provides much more
00628  * stability than its predecessor.
00629  */
00630 static uint8_t NicOverflow(volatile uint8_t * base)
00631 {
00632     u_int cr;
00633     u_int resend = 0;
00634     u_int curr;
00635 
00636     /*
00637      * Wait for any transmission in progress. Save the command register, 
00638      * so we can later determine, if NIC transmitter has been interrupted. 
00639      * or reception in progress.
00640      */
00641     while (Asix_Read(CR) & CR_TXP);
00642     cr = Asix_Read(CR);
00643 
00644     /*
00645      * Get the current page pointer. It points to the page where the NIC 
00646      * will start saving the next incoming packet.
00647      */
00648     Asix_Write(CR, CR_STOP | CR_RD2 | CR_PS0);
00649     curr = Asix_Read(PG1_CPR);
00650     Asix_Write(CR, CR_STOP | CR_RD2);
00651 
00652     /* Clear remote byte count register. */
00653     Asix_Write(PG0_RBCR0, 0);
00654     Asix_Write(PG0_RBCR1, 0);
00655 
00656     /* Check for any incomplete transmission. */
00657     if ((cr & CR_TXP) && ((Asix_Read(PG0_ISR) & (ISR_PTX | ISR_TXE)) == 0)) {
00658         resend = 1;
00659     }
00660 
00661     /* Enter loopback mode and restart the NIC. */
00662     Asix_Write(PG0_TCR, TCR_LB0);
00663     Asix_Write(CR, CR_START | CR_RD2);
00664 
00665     /* 
00666      * Discard all packets from the receiver buffer. Set boundary 
00667      * register to the last page we read. 
00668      */
00669     if (--curr < TXSTART_INIT) {
00670         curr = RXSTOP_INIT - 1;
00671     }
00672     Asix_Write(PG0_BNRY, curr);
00673 
00674     /* Switch from loopback to normal mode mode. */
00675     Asix_Write(PG0_TCR, 0);
00676 
00677     /* Re-invoke any interrupted transmission. */
00678     if (resend) {
00679         Asix_Write(CR, CR_START | CR_TXP | CR_RD2);
00680     }
00681 
00682     /* Finally clear the overflow flag */
00683     Asix_Write(PG0_ISR, ISR_OVW);
00684     return resend;
00685 }
00686 
00687 //==============================================================================
00688 static int NicPutPacket(NETBUF * nb)
00689 {
00690     uint16_t sz;                 // packed size
00691     uint16_t send_sz;            // send packed size, min 60
00692     static uint8_t first_put = 0;
00693     int tmp;
00694 
00695 
00696     /*
00697      * Fix problem after power on.
00698      */
00699     if (first_put != 1) {
00700         Asix_Write(CR, 0x21);
00701         NutDelay(1);
00702         Asix_Write(CR, 0x22);
00703         first_put = 1;
00704     }
00705 
00706     /*
00707      * Calculate the number of bytes to be send. Do not
00708      * send packets larger than 1518 bytes.
00709      */
00710     sz = nb->nb_dl.sz + nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz;
00711     if (sz > 1518)
00712         return -1;
00713     //printf("Asix Send %u\n", sz);
00714 
00715     /*
00716      * Calculate the number of min bytes. Pad will be
00717      * added when packet lenght less than 60. Enable in TCR reg. PD = 0. 
00718      */
00719     send_sz = sz;
00720     if (sz <= 60)
00721         send_sz = 60;
00722 
00723     /* Disable Asix interrupts. */
00724     NutEnterCritical();
00725     /* start the NIC */
00726     Asix_Write(CR, (CR_RD2 | CR_START));
00727 
00728     /*
00729      * Bengt Florin introduces polling mode for the transmitter. Be
00730      * aware, that this may introduce other problems. If a high
00731      * priority thread is waiting for the transmitter, it may hold
00732      * the CPU for more than 1.2 milliseconds in worst cases.
00733      */
00734     tmp = 120;
00735     while ((Asix_Read(CR) & CR_TXP) && tmp--)
00736         NutDelay(1);
00737 
00738 
00739     /* set start address for remote DMA operation */
00740     Asix_Write(PG0_RSAR0, 0x00);
00741     Asix_Write(PG0_RSAR1, TXSTART_INIT);
00742 
00743     /* load data byte count for remote DMA */
00744     Asix_Write(PG0_RBCR0, (unsigned char) (sz));
00745     Asix_Write(PG0_RBCR1, (unsigned char) (sz >> 8));
00746 
00747     /* do remote write operation */
00748     Asix_Write(CR, (CR_RD1 | CR_START));
00749 
00750     /* Transfer the Ethernet frame. */
00751     NicWrite(nb->nb_dl.vp, nb->nb_dl.sz);
00752     NicWrite(nb->nb_nw.vp, nb->nb_nw.sz);
00753     NicWrite(nb->nb_tp.vp, nb->nb_tp.sz);
00754     NicWrite(nb->nb_ap.vp, nb->nb_ap.sz);
00755 
00756     /* Complete remote dma. */
00757     NicCompleteDma();
00758 
00759     /* Clear interrupt flags. */
00760     Asix_Write(PG0_ISR, (ISR_PTX | ISR_TXE));
00761     Delay16Cycles();
00762 
00763     /* Set size and pointer to data. */
00764     Asix_Write(CR, CR_START | CR_RD2);
00765     //printf("Asix Start Send\n");
00766     Asix_Write(PG0_TBCR0, (unsigned char) (send_sz));
00767     Asix_Write(PG0_TBCR1, (unsigned char) ((send_sz) >> 8));
00768     Asix_Write(PG0_TPSR, TXSTART_INIT);
00769 
00770     /* Start transmission. */
00771     Asix_Write(CR, CR_START | CR_TXP | CR_RD2);
00772 
00773     /* Enable Asix interrupts. */
00774     NutExitCritical();
00775     return 0;
00776 }
00777 
00778 //==============================================================================
00783 THREAD(NicRxAsix, arg)
00784 {
00785 
00786     NUTDEVICE *dev;
00787     IFNET *ifn;
00788     NICINFO *ni;
00789     NETBUF *nb;
00790 
00791     /* Hack! Unfortunately no parameter passing with ARM */
00792     dev = &devAx88796;
00793 
00794     ifn = (IFNET *) dev->dev_icb;
00795     ni = (NICINFO *) dev->dev_dcb;
00796 
00797     for (;;) {
00798         if (*((uint32_t *) (ifn->if_mac)) && *((uint32_t *) (ifn->if_mac)) != 0xFFFFFFFFUL) {
00799             break;
00800         }
00801         NutSleep(63);
00802     }
00803 
00804     NutEnterCritical();
00805     NicStart(ifn->if_mac);
00806     NutExitCritical();
00807 
00808     /* Run at high priority. */
00809     //printf("Increase prio\n");
00810     NutThreadSetPriority(9);
00811 
00812     while (1) {
00813         //printf("-------- Wait rx\n");
00814         NutEventWait(&ni->ni_rx_rdy, 0);
00815         //printf("-------- Got rx\n");
00816         /*
00817          * Fetch all packets from the NIC's internal
00818          * buffer and pass them to the registered handler.
00819          */
00820         do {
00821             nb = NicGetPacket();
00822             /* The sanity check may fail because the controller is too busy.
00823                restart the NIC. */
00824             if (0) {            //HK if ((u_short)nb == 0xFFFF) {
00825                 NicStart(ifn->if_mac);
00826 //                ni->ni_rx_size_errors++;
00827             } else if (nb) {
00828                 ni->ni_rx_packets++;
00829                 (*ifn->if_recv) (dev, nb);
00830             }
00831         } while (nb);
00832     }
00833 }
00834 
00835 //==============================================================================
00836 /*
00837  * NIC interrupt entry.
00838  */
00839 static void NicInterrupt(void *arg)
00840 {
00841     uint8_t isr;
00842     volatile uint8_t *base = (uint8_t *) (((NUTDEVICE *) arg)->dev_base);
00843     NICINFO *ni = (NICINFO *) ((NUTDEVICE *) arg)->dev_dcb;
00844 
00845     ni->ni_interrupts++;
00846 
00847     isr = Asix_Read(PG0_ISR);
00848     Delay16Cycles();
00849     Asix_Write(PG0_ISR, isr);
00850 
00851     //if(isr)
00852     //    printf("%02X\n", isr);
00853     //else
00854     //    return;
00855 
00856     /*
00857      * Recover from receive buffer overflow. This may take some
00858      * time, so we enable global interrupts but keep NIC
00859      * interrupts disabled.
00860      */
00861     if (isr & ISR_OVW) {
00862         ni->ni_rx_pending++;
00863         if (NicOverflow(base))
00864             ni->ni_tx_bsy++;
00865         else {
00866             NutEventPostAsync(&ni->ni_tx_rdy);
00867         }
00868         ni->ni_overruns++;
00869     } else {
00870 
00871         /*
00872          * If this is a transmit interrupt, then a packet has been sent. 
00873          * So we can clear the transmitter busy flag and wake up the 
00874          * transmitter thread.
00875          */
00876         if (isr & (ISR_PTX | ISR_TXE)) {
00877             ni->ni_tx_bsy = 0;
00878         }
00879 
00880         /*
00881          * If this is a receive interrupt, then wake up the receiver 
00882          * thread.
00883          */
00884         if (isr & ISR_PRX) {
00885             ni->ni_rx_pending++;
00886             //printf("Post %lX\n", (uint32_t) ni->ni_rx_rdy);
00887             NutEventPostFromIrq(&ni->ni_rx_rdy);
00888         }
00889 
00890         /* Rx error. */
00891         if (isr & ISR_RXE) {
00892             if (Asix_Read(PG0_RSR) & RSR_FAE)
00893                 ni->ni_rx_frame_errors++;
00894             if (Asix_Read(PG0_RSR) & RSR_CR)
00895                 ni->ni_rx_crc_errors++;
00896             if (Asix_Read(PG0_RSR) & RSR_MPA)
00897                 ni->ni_rx_missed_errors++;
00898         }
00899     }
00900 }
00901 
00902 void NicInterruptEntry(void) __attribute__ ((naked));
00903 void NicInterruptEntry(void)
00904 {
00905     IRQ_ENTRY();
00906     //putchar('i');
00907     NicInterrupt(&devAx88796);
00908     IRQ_EXIT();
00909 }
00910 
00911 //==============================================================================
00922 int AsixOutput(NUTDEVICE * dev, NETBUF * nb)
00923 {
00924     int rc = -1;
00925     NICINFO *ni = (NICINFO *) dev->dev_dcb;
00926 
00927     if (NicPutPacket(nb) == 0) {
00928         ni->ni_tx_packets++;
00929         rc = 0;
00930     }
00931     return rc;
00932 }
00933 
00934 //==============================================================================
00952 int AsixInit(NUTDEVICE * dev)
00953 {
00954     //printf("Set confnet\n");
00955     confnet.cd_size = sizeof(CONFNET);
00956     strcpy(confnet.cd_name, "eth0");
00957     memset(confnet.cdn_mac, 0xFF, 6);
00958 
00959     /* Disable NIC interrupt and clear NICINFO structure. */
00960     //printf("Clear nicinfo\n");
00961     memset(dev->dev_dcb, 0, sizeof(NICINFO));
00962 
00963     /*
00964      * Start the receiver thread.
00965      */
00966     //printf("Create rxi5(%lX)\n", (u_long) dev);
00967     NutThreadCreate("rxi5", NicRxAsix, dev, 1024);
00968 
00969     outr(PIO_PDR, _BV(9));
00970     /* Register interrupt handler and enable interrupts. */
00971     /* Disable timer 0 interrupts. */
00972     outr(AIC_IDCR, _BV(IRQ0_ID));
00973     /* Set the TC0 IRQ handler address */
00974     outr(AIC_SVR(IRQ0_ID), (unsigned int) NicInterruptEntry);
00975     /* Set the trigg and priority for timer 0 interrupt */
00976     /* Level 7 is highest, level 0 lowest. */
00977     outr(AIC_SMR(IRQ0_ID), (AIC_SRCTYPE_EXT_NEGATIVE_EDGE | 5));
00978     /* Clear timer 0 interrupt */
00979     outr(AIC_ICCR, _BV(IRQ0_ID));
00980     /* Enable timer 0 interrupts */
00981     outr(AIC_IECR, _BV(IRQ0_ID));
00982 
00983     //printf("======== AsixcInit done ========\n");
00984     return 0;
00985 }
00986 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/