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