cs8900a.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 
00034 /*
00035  * $Log: cs8900a.c,v $
00036  * Revision 1.4  2008/08/11 06:59:07  haraldkipp
00037  * BSD types replaced by stdint types (feature request #1282721).
00038  *
00039  * Revision 1.3  2007/05/02 11:22:51  haraldkipp
00040  * Added multicast table entry.
00041  *
00042  * Revision 1.2  2006/05/25 09:09:57  haraldkipp
00043  * API documentation updated and corrected.
00044  *
00045  * Revision 1.1  2006/02/23 15:33:59  haraldkipp
00046  * Support for Philips LPC2xxx Family and LPC-E2294 Board from Olimex added.
00047  * Many thanks to Michael Fischer for this port.
00048  *
00049  * Revision 1.2  2005/08/02 17:46:45  haraldkipp
00050  * Major API documentation update.
00051  *
00052  * Revision 1.1  2005/07/26 18:02:40  haraldkipp
00053  * Moved from dev.
00054  *
00055  * Revision 1.13  2005/04/30 16:42:41  chaac
00056  * Fixed bug in handling of NUTDEBUG. Added include for cfg/os.h. If NUTDEBUG
00057  * is defined in NutConf, it will make effect where it is used.
00058  *
00059  * Revision 1.12  2005/02/02 19:55:34  haraldkipp
00060  * If no Ethernet link was available on the LAN91C111, each outgoing packet
00061  * took 15 seconds and, even worse, the ouput routine doesn't return an error.
00062  * Now the first attempt to send a packet without Ethernet link will wait for
00063  * 5 seconds and subsequent attempts take 0.5 seconds only, always returning
00064  * an error.
00065  *
00066  * Revision 1.11  2005/01/24 21:11:49  freckle
00067  * renamed NutEventPostFromIRQ into NutEventPostFromIrq
00068  *
00069  * Revision 1.10  2005/01/22 19:24:11  haraldkipp
00070  * Changed AVR port configuration names from PORTx to AVRPORTx.
00071  *
00072  * Revision 1.9  2005/01/21 16:49:45  freckle
00073  * Seperated calls to NutEventPostAsync between Threads and IRQs
00074  *
00075  * Revision 1.8  2004/09/22 08:14:48  haraldkipp
00076  * Made configurable
00077  *
00078  * Revision 1.7  2004/03/08 11:14:17  haraldkipp
00079  * Added quick hack for fixed mode.
00080  *
00081  * Revision 1.6  2004/02/25 16:22:33  haraldkipp
00082  * Do not initialize MAC with all zeros
00083  *
00084  * Revision 1.5  2004/01/14 19:31:43  drsung
00085  * Speed improvement to NicWrite applied. Thanks to Kolja Waschk
00086  *
00087  * Revision 1.4  2003/11/06 09:26:50  haraldkipp
00088  * Removed silly line with hardcoded MAC, left over from testing
00089  *
00090  * Revision 1.3  2003/11/04 17:54:47  haraldkipp
00091  * PHY configuration timing changed again for reliable linking
00092  *
00093  * Revision 1.2  2003/11/03 17:12:53  haraldkipp
00094  * Allow linking with RTL8019 driver.
00095  * Links more reliable to 10 MBit networks now.
00096  * Reset MMU on allocation failures.
00097  * Some optimizations.
00098  *
00099  * Revision 1.1  2003/10/13 10:13:49  haraldkipp
00100  * First release
00101  *
00102  */
00103 
00104 #include <cfg/os.h>
00105 #include <cfg/arch/avr.h>
00106 
00107 #include <string.h>
00108 
00109 #include <sys/atom.h>
00110 #include <sys/heap.h>
00111 #include <sys/thread.h>
00112 #include <sys/event.h>
00113 #include <sys/timer.h>
00114 #include <sys/confnet.h>
00115 
00116 #include <netinet/if_ether.h>
00117 #include <net/ether.h>
00118 #include <net/if_var.h>
00119 
00120 #include <dev/irqreg.h>
00121 #include <dev/cs8900a.h>
00122 
00123 #ifdef NUTDEBUG
00124 #include <stdio.h>
00125 #endif
00126 
00127 /*
00128  * This file is for the Olimex LPC-E2294 board @@MF
00129  */
00130 #include <arch/arm/lpc2xxx.h>
00131 #include <__armlib.h>
00132 #define cli()   __ARMLIB_disableIRQ()
00133 #define sei()   __ARMLIB_enableIRQ()   
00134 
00135 /*=========================================================================*/
00136 /*  DEFINE: All Structures and Common Constants                            */
00137 /*=========================================================================*/
00138 
00139 /* 
00140  * Cirrus Logic CS8900a I/O Registers
00141  */
00142 #define CS_DATA_P0    (cs_base + 0x0000UL)
00143 #define CS_DATA_P1      (cs_base + 0x0002UL)
00144 #define CS_TX_CMD_I     (cs_base + 0x0004UL)
00145 #define CS_TX_LEN_I     (cs_base + 0x0006UL)
00146 #define CS_INT_STAT   (cs_base + 0x0008UL)
00147 #define CS_PP_PTR         (cs_base + 0x000AUL)
00148 #define CS_PP_DATA0     (cs_base + 0x000CUL)
00149 #define CS_PP_DATA1     (cs_base + 0x000EUL)
00150 
00151 
00152 // Cirrus Logic CS8900a Packet Page registers
00153 #define CS_PROD_ID      0x0000
00154 #define CS_IO_BASE      0x0020
00155 #define CS_INT_NUM      0x0022
00156 #define CS_DMA_CHAN     0x0024
00157 #define CS_DMA_SOF      0x0026
00158 #define CS_DMA_FCNT     0x0028
00159 #define CS_DMA_RXCNT    0x002A
00160 #define CS_MEM_BASE     0x002C
00161 #define CS_BOOT_BASE    0x0030
00162 #define CS_BOOT_MASK    0x0034
00163 #define CS_EE_CMD         0x0040
00164 #define CS_EE_DATA      0x0042
00165 #define CS_RX_FRM_CNT   0x0050
00166 
00167 #define CS_ISQ            0x0120
00168 #define CS_RX_CFG         0x0102
00169 #define CS_RX_EVENT     0x0124
00170 #define CS_RX_CTL         0x0104
00171 #define CS_TX_CFG         0x0106
00172 #define CS_TX_EVENT     0x0128
00173 #define CS_TX_CMD_P     0x0108
00174 #define CS_BUF_CFG      0x010A
00175 #define CS_BUF_EVENT    0x012C
00176 #define CS_RX_MISS      0x0130
00177 #define CS_TX_COLL      0x0132
00178 #define CS_LINE_CTRL    0x0112
00179 #define CS_LINE_STAT    0x0134
00180 #define CS_SELF_CTRL    0x0114
00181 #define CS_SELF_STAT    0x0136
00182 #define CS_BUS_CTRL     0x0116
00183 #define CS_BUS_STAT     0x0138
00184 #define CS_TEST_CTRL    0x0118
00185 #define CS_AUI_TDR      0x013C
00186 
00187 #define CS_PP_TX_CMD    0x0144
00188 #define CS_PP_TX_LEN    0x0146
00189 
00190 #define CS_IEEE_ADDR    0x0158
00191 
00196 
00201 struct _NICINFO {
00202     uint32_t ni_rx_packets;       
00203     uint32_t ni_tx_packets;       
00204 };
00205 
00209 typedef struct _NICINFO NICINFO;
00210 
00211 /*=========================================================================*/
00212 /*  DEFINE: Definition of all local Data                                   */
00213 /*=========================================================================*/
00214 // Ethernet flags byte
00215 // Bit 0 = transmit byte flag
00216 static   uint8_t cs_flags = 0;
00217 volatile uint32_t cs_base  = 0x82000000UL;
00218 
00219 
00220 /*=========================================================================*/
00221 /*  DEFINE: Definition of all local Procedures                             */
00222 /*=========================================================================*/
00223 void CSWrite16(uint32_t addr, uint16_t data)
00224 {
00225     uint8_t *p;
00226 
00227     p = (uint8_t *) addr;
00228     cli();
00229     *p = data;
00230     p++;
00231     *p = data >> 8;    
00232     sei();
00233 }
00234 
00235 void CSWritePP16(uint16_t addr, uint16_t data)
00236 {
00237     uint8_t *p;
00238 
00239     cli();
00240     p = (uint8_t *) CS_PP_PTR;
00241     *p = addr;
00242     p++;
00243     *p = addr >> 8;
00244 
00245     CSWrite16(CS_PP_DATA0, data);
00246 
00247     return;
00248 }
00249 
00250 uint16_t CSRead16(uint32_t addr)
00251 {
00252     uint8_t *p;
00253     uint16_t d;
00254 
00255     cli();
00256     p  = (uint8_t *) addr;
00257     d  = *p;
00258     p++;
00259     d |= (*p << 8); 
00260     sei();
00261 
00262     return d;
00263 }
00264 
00265 uint16_t CSReadPP16(uint16_t addr)
00266 {
00267     uint8_t *p;
00268 
00269     cli();
00270     p = (uint8_t *) CS_PP_PTR;
00271     *p = addr;
00272     p++;
00273     *p = addr >> 8;
00274 
00275     return CSRead16(CS_PP_DATA0);
00276 }
00277 
00278 void CSBeginFrame(void)
00279 {
00280     cs_flags &= ~1;
00281 }
00282 
00283 void CSEndFrame(void)
00284 {
00285     uint8_t *p;
00286 
00287     cli();
00288     p = (uint8_t *) CS_DATA_P0 + 1;
00289     sei();
00290 
00291     // If odd number of bytes in packet pad it out
00292     if (cs_flags & 1)
00293         p = 0;
00294 }
00295 
00296 void CSWriteFrameByte(uint8_t data)
00297 {
00298     uint8_t *p;
00299 
00300     if (cs_flags & 1)
00301         p = (uint8_t *) CS_DATA_P0 + 1;
00302     else
00303         p = (uint8_t *) CS_DATA_P0;
00304 
00305     *p = data;
00306     cs_flags ^= 1;
00307 }
00308 
00309 static int CSEthPutPacket(NUTDEVICE * dev, NETBUF * nb)
00310 {
00311     uint16_t i;
00312     uint16_t sz;
00313     uint8_t *p;
00314     NICINFO *ni;
00315 
00316     ni = (NICINFO *) dev->dev_dcb;
00317 
00318     //
00319     // Calculate the number of bytes to be send. Do not
00320     // send packets larger than 1536 bytes.
00321     //
00322     sz = nb->nb_dl.sz + nb->nb_nw.sz + nb->nb_tp.sz + nb->nb_ap.sz;
00323     if (sz >= 0x600) {
00324         NutNetBufFree(nb);
00325         return -1;
00326     }
00327 #if 0
00328     if (tcp_trace) {
00329         NutPrintFormat_P(dev_debug, PSTR("[ETHTX-%u]\r\n"), sz);
00330         NutPrintFlush(dev_debug);
00331     }
00332 #endif
00333 
00334     // Start transmission after entire frame is loaded into CS8900
00335     CSWrite16(CS_TX_CMD_I, 0xC0);
00336     // Set frame size
00337     CSWrite16(CS_TX_LEN_I, sz);
00338 
00339     // Wait for buffer space, but only for a while (200ms)
00340     // If the cable is disconnected this will never become true
00341     // If we don't get the go ahead within 200ms return 0 (Sucess)
00342     // And let the upper layers deal with re-transmission 
00343     // If we return failure TCP sockets will close straight away which probably
00344     // isn't the correct behaviour
00345     i = 0;
00346     while ((CSReadPP16(CS_BUS_STAT) & 0x0100) == 0) {
00347         i++;
00348         if (i > 20)
00349             return 0;
00350         NutSleep(10);
00351     }
00352 
00353     //
00354     // Transfer ethernet physical header.
00355     //
00356     CSBeginFrame();
00357 
00358     p = nb->nb_dl.vp;
00359     for (i = 0; i < nb->nb_dl.sz; i++) {
00360         CSWriteFrameByte(*p++);
00361     }
00362 
00363 
00364     p = nb->nb_nw.vp;
00365     for (i = 0; i < nb->nb_nw.sz; i++) {
00366         CSWriteFrameByte(*p++);
00367     }
00368 
00369     p = nb->nb_tp.vp;
00370     for (i = 0; i < nb->nb_tp.sz; i++) {
00371         CSWriteFrameByte(*p++);
00372     }
00373 
00374     p = nb->nb_ap.vp;
00375     for (i = 0; i < nb->nb_ap.sz; i++) {
00376         CSWriteFrameByte(*p++);
00377     }
00378 
00379     CSEndFrame();
00380 
00381     return 0;
00382 }
00383 
00384 void CSSoftwareWakeup(void)
00385 {
00386     volatile uint8_t *p;
00387     uint16_t          data = CS_SELF_CTRL;
00388 
00389     p = (uint8_t *) CS_PP_PTR;
00390     *p = data;
00391     p++;
00392     *p = data >> 8;
00393 
00394     NutDelay(10);
00395 }
00396 
00397 
00398 void CSSoftwareReset(void)
00399 {
00400     volatile uint8_t *p;
00401     uint16_t          data;
00402 
00403     data = CS_SELF_CTRL;
00404     p  = (uint8_t *) CS_PP_PTR;    
00405     *p = data;
00406     p++;
00407     *p = data >> 8;
00408         
00409     data = 0x0040;
00410     p  = (uint8_t *) CS_DATA_P0;
00411     *p = data;
00412     p++;
00413     *p = data >> 8;
00414 }
00415 
00416 
00417 THREAD(CSNICrx, arg)
00418 {
00419     NUTDEVICE *dev;
00420     IFNET *ifn;
00421     NICINFO *ni;
00422     NETBUF *nb;
00423     uint8_t *p;
00424     uint8_t *q;
00425     uint16_t i, m;
00426     volatile uint16_t l;
00427 
00428     dev = arg;
00429     ifn = (IFNET *) dev->dev_icb;
00430     ni = (NICINFO *) dev->dev_dcb;
00431 
00432 #if 0
00433     if (tcp_trace) {
00434         NutPrintFormat_P(dev_debug, PSTR("Enter ETHReceive\r\n"));
00435         NutPrintFlush(dev_debug);
00436     }
00437 #endif
00438 
00439     l = 0;
00440 
00441     NutThreadSetPriority(8);
00442     for (;;) {
00443         while ((CSReadPP16(CS_RX_EVENT) & 0x0100) == 0) {
00444             NutSleep(10);
00445         }
00446 
00447         l = *(uint8_t *) (CS_DATA_P0 + 1) << 8 | *(uint8_t *) (CS_DATA_P0);
00448         l = *(uint8_t *) (CS_DATA_P0 + 1) << 8 | *(uint8_t *) (CS_DATA_P0);
00449         
00450         //NutPrintFormat_P(dev_debug,PSTR("RxLength = %x \r\n"), l);
00451         //NutPrintFlush(dev_debug);
00452 
00453         // Account for odd length packets
00454         if (l & 1)
00455             m = l - 1;
00456         else
00457             m = l;
00458 
00459 
00460         nb = NutNetBufAlloc(0, NBAF_DATALINK, l);
00461         if (nb) {
00462             q = nb->nb_dl.vp;
00463             for (i = 0; i < m; i += 2) {
00464                 p = (uint8_t *) CS_DATA_P0;
00465                 *q++ = *p;
00466                 p = (uint8_t *) CS_DATA_P0 + 1;
00467                 *q++ = *p;
00468             }
00469 
00470             // Odd length packets
00471             if (m != l) {
00472                 p = (uint8_t *) CS_DATA_P0;
00473                 *q++ = *p;
00474 
00475                 p = (uint8_t *) CS_DATA_P0 + 1;
00476                 m = *p;
00477             }
00478             ni->ni_rx_packets++;
00479             (*ifn->if_recv) (dev, nb);
00480         }
00481     }
00482 }
00483 
00484 /*=========================================================================*/
00485 /*  DEFINE: All code exported                                              */
00486 /*=========================================================================*/
00487 
00488 
00489 
00500 int cs8900Output(NUTDEVICE * dev, NETBUF * nb)
00501 {
00502     int rc = -1;
00503     NICINFO *ni;
00504 
00505     ni = (NICINFO *) dev->dev_dcb;
00506 
00507 #if 0
00508     if (tcp_trace) {
00509         NutPrintFormat_P(dev_debug, PSTR("Enter EthOutput\r\n"));
00510         NutPrintFlush(dev_debug);
00511     }
00512 #endif
00513 
00514     if ((rc = CSEthPutPacket(dev, nb)) == 0)
00515         ni->ni_tx_packets++;
00516 
00517     return rc;
00518 }
00519 
00537 int cs8900Init(NUTDEVICE * dev)
00538 {
00539     uint16_t  i;
00540     uint16_t  j;
00541     IFNET   *ifn;
00542     NICINFO *ni;
00543     
00544 #if 0
00545     if (tcp_trace) {
00546         NutPrintFormat_P(dev_debug, PSTR("Enter NicInit  \r\n"));
00547         NutPrintFlush(dev_debug);
00548     }
00549 #endif
00550 
00551     cs_base = dev->dev_base;
00552     
00553 #if defined(OLIMEX_LPCE2294)
00554     if (cs_base == 0)
00555     {
00556       cs_base = 0x82000000UL;
00557     }
00558 #endif    
00559 
00560 
00561     if (confnet.cd_size == 0)
00562         NutNetLoadConfig(dev->dev_name);
00563 
00564     ifn = dev->dev_icb;
00565 #if 0 /* @@MF */
00566     memcpy(ifn->if_mac, confnet.cdn_mac, 6);
00567 #else
00568     ifn->if_mac[0] = 0x00;
00569     ifn->if_mac[1] = 0x06;
00570     ifn->if_mac[2] = 0x98;
00571     ifn->if_mac[3] = 0x00;
00572     ifn->if_mac[4] = 0x00;
00573     ifn->if_mac[5] = 0x00;
00574 #endif    
00575     memset(dev->dev_dcb, 0, sizeof(NICINFO));
00576     ni = (NICINFO *) dev->dev_dcb;
00577 
00578     // Take CS8900 out of reset and wait for internal reset to complete
00579     CSSoftwareWakeup();
00580     CSSoftwareReset();
00581 
00582     NutDelay(100);
00583 
00584     // Check for presence
00585     if (CSReadPP16(CS_PROD_ID) != 0x630E)
00586         return -1;
00587 
00588     //
00589     //  Copy our MAC address to the NIC
00590     // 
00591     for (i = 0; i < 6; i += 2) {
00592         j = ifn->if_mac[i] << 8;
00593         j |= ifn->if_mac[i + 1];
00594         CSWritePP16(CS_IEEE_ADDR + i, j);
00595         j = CSReadPP16(CS_IEEE_ADDR + i);
00596 #if 0
00597         if (tcp_trace) {
00598             NutPrintFormat_P(dev_debug, PSTR("ADDR = %x\r\n"), j);
00599             NutPrintFlush(dev_debug);
00600         }
00601 #endif
00602     }
00603 
00604     //
00605     // Enable the Transmitter and Receiver
00606     //
00607     CSWritePP16(CS_LINE_CTRL, 0x00C0);
00608     //i = CSReadPP16(CS_LINE_CTRL);
00609     //NutPrintFormat_P(dev_debug,PSTR("CS_LINE_CTRL = %x\r\n"), i);
00610 
00611     CSWritePP16(CS_RX_CTL, 0x0F40);
00612     //i = CSReadPP16(CS_RX_CTL);
00613     //NutPrintFormat_P(dev_debug,PSTR("CS_RX_CTL = %x\r\n"), i);
00614 
00615     // 
00616     // Start receiver thread
00617     //
00618     NutThreadCreate("csnicrx", CSNICrx, dev, 768);
00619 
00620     return 0;
00621 }
00622 
00623 static NICINFO dcb_eth0;
00624 
00630 static IFNET ifn_eth0 = {
00631     IFT_ETHER,                  
00632     {0, 0, 0, 0, 0, 0},         
00633     0,                          
00634     0,                          
00635     0,                          
00636     ETHERMTU,                   
00637     0,                          
00638     0,                          
00639     0,                          
00640     NutEtherInput,              
00641     cs8900Output,               
00642     NutEtherOutput              
00643 };
00644 
00654 NUTDEVICE devCS8900A = {
00655     0,                          /* Pointer to next device. */
00656     {'e', 't', 'h', '0', 0, 0, 0, 0, 0},        /* Unique device name. */
00657     IFTYP_NET,                  /* Type of device. */
00658     0,                          /* Base address. */
00659     0,                          /* First interrupt number. */
00660     &ifn_eth0,                  /* Interface control block. */
00661     &dcb_eth0,                  /* Driver control block. */
00662     cs8900Init,                 /* Driver initialization routine. */
00663     0,                          /* Driver specific control function. */
00664     0,                          /* Read from device. */
00665     0,                          /* Write to device. */
00666     0,                          /* Open a device or file. */
00667     0,                          /* Close a device or file. */
00668     0                           /* Request file size. */
00669 };
00670 

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