diff packages/devs/eth/arm/edb7xxx/current/src/if_edb7xxx.c @ 82:6736c52df507 ecos-sw-2000-04-14

Merge from eCos master repository on 2000-04-14-13:35:46-BST
author jlarmour
date Tue, 18 Apr 2000 21:51:55 +0000
parents
children 6ed91473a1cd
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/devs/eth/arm/edb7xxx/current/src/if_edb7xxx.c
@@ -0,0 +1,420 @@
+//==========================================================================
+//
+//      dev/if_edb7xxx.c
+//
+//      Ethernet device driver for Cirrus Logic EDB7xxx using CS8900
+//
+//==========================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// -------------------------------------------                              
+// The contents of this file are subject to the Red Hat eCos Public License 
+// Version 1.1 (the "License"); you may not use this file except in         
+// compliance with the License.  You may obtain a copy of the License at    
+// http://www.redhat.com/                                                   
+//                                                                          
+// Software distributed under the License is distributed on an "AS IS"      
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
+// License for the specific language governing rights and limitations under 
+// the License.                                                             
+//                                                                          
+// The Original Code is eCos - Embedded Configurable Operating System,      
+// released September 30, 1998.                                             
+//                                                                          
+// The Initial Developer of the Original Code is Red Hat.                   
+// Portions created by Red Hat are                                          
+// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.                             
+// All Rights Reserved.                                                     
+// -------------------------------------------                              
+//                                                                          
+//####COPYRIGHTEND####
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD or other sources,
+// and are covered by the appropriate copyright disclaimers included herein.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    gthomas
+// Contributors: gthomas
+// Date:         2000-01-10
+// Purpose:      
+// Description:  hardware driver for CS8900 ethernet
+//              
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+// Ethernet device driver for Cirrus Logic EDB7xxx
+// Based on CS8900A
+
+#include <pkgconf/net.h>
+#include <cyg/infra/cyg_type.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/infra/diag.h>
+#include <cyg/hal/drv_api.h>
+#include <netdev.h>
+#include <eth_drv.h>
+
+#define INTS_DONT_WORK
+#undef  INTS_DONT_WORK
+
+#ifdef INTS_DONT_WORK
+#define STACK_SIZE CYGNUM_HAL_STACK_SIZE_MINIMUM
+static char cs8900_fake_int_stack[STACK_SIZE];
+static cyg_thread cs8900_fake_int_thread_data;
+static cyg_handle_t cs8900_fake_int_thread_handle;
+static void cs8900_fake_int(cyg_addrword_t);
+#endif
+
+#define  CS8900_BASE 0x20000000
+#include "cs8900.h"
+#define ETHER_ADDR_LEN 6
+
+extern int net_debug; // FIXME
+
+struct cs8900_priv_data {
+    int txbusy;            // A packet has been sent
+    unsigned long txkey;   // Used to ack when packet sent
+} _cs8900_priv_data;
+
+ETH_DRV_SC(edb7xxx_sc,
+           &_cs8900_priv_data, // Driver specific data
+           "eth0",             // Name for this interface
+           cs8900_start,
+           cs8900_stop,
+           cs8900_control,
+           cs8900_can_send,
+           cs8900_send,
+           cs8900_recv);
+
+NETDEVTAB_ENTRY(edb7xxx_netdev, 
+                "edb7xxx", 
+                edb7xxx_cs8900_init, 
+                &edb7xxx_sc);
+
+// FIXME
+static unsigned char enaddr[] = { 0x08, 0x88, 0x12, 0x34, 0x56, 0x78};
+
+static void cs8900_int(struct eth_drv_sc *sc);
+static cyg_interrupt cs8900_interrupt;
+static cyg_handle_t  cs8900_interrupt_handle;
+
+// This ISR is called when the ethernet interrupt occurs
+static int
+cs8900_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters *regs)
+{
+    cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_EINT3);
+    return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR
+}
+
+// This DSR handles the ethernet [logical] processing
+static void
+cs8900_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+    cs8900_int((struct eth_drv_sc *)data);
+    // Allow interrupts to happen again
+    cyg_drv_interrupt_acknowledge(CYGNUM_HAL_INTERRUPT_EINT3);
+    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_EINT3);
+}
+
+static bool 
+edb7xxx_cs8900_init(struct cyg_netdevtab_entry *tab)
+{
+    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
+    unsigned short chip_type, chip_rev, chip_status;
+    int i;
+
+    diag_printf("cs8900 init\n");
+
+    // Initialize environment, setup interrupt handler
+    cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_EINT3,
+                             99, // Priority - what goes here?
+                             (cyg_addrword_t)sc, //  Data item passed to interrupt handler
+                             (cyg_ISR_t *)cs8900_isr,
+                             (cyg_DSR_t *)cs8900_dsr,
+                             &cs8900_interrupt_handle,
+                             &cs8900_interrupt);
+    cyg_drv_interrupt_attach(cs8900_interrupt_handle);
+    cyg_drv_interrupt_acknowledge(CYGNUM_HAL_INTERRUPT_EINT3);
+    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_EINT3);
+
+#ifdef INTS_DONT_WORK
+    cyg_thread_create(1,                 // Priority
+                      cs8900_fake_int,   // entry
+                      0,                 // entry parameter
+                      "CS8900 int",      // Name
+                      &cs8900_fake_int_stack[0],         // Stack
+                      STACK_SIZE,        // Size
+                      &cs8900_fake_int_thread_handle,    // Handle
+                      &cs8900_fake_int_thread_data       // Thread data structure
+            );
+    cyg_thread_resume(cs8900_fake_int_thread_handle);  // Start it
+#endif
+
+    chip_type = get_reg(PP_ChipID);
+    chip_rev = get_reg(PP_ChipRev);
+
+    diag_printf("CS8900 - type: %x, rev: %x\n", chip_type, chip_rev);
+
+    put_reg(PP_SelfCtl, PP_SelfCtl_Reset);  // Reset chip
+    while ((get_reg(PP_SelfStat) & PP_SelfStat_InitD) == 0) ;  
+
+    chip_status = get_reg(PP_SelfStat);
+    diag_printf("CS8900 - status: %x (%sEEPROM present)\n", chip_status,
+                chip_status&PP_SelfStat_EEPROM ? "" : "no ");
+
+    // Set up hardware address - FIXME
+    for (i = 0;  i < ETHER_ADDR_LEN;  i += 2) {
+        put_reg(PP_LAF+i, 0xFFFF);
+        put_reg(PP_IA+i, enaddr[i] | (enaddr[i+1] << 8));
+    }
+
+    // Initialize upper level driver
+    eth_drv_init(sc, enaddr);
+
+    return true;
+}
+
+static void
+cs8900_stop(struct eth_drv_sc *sc)
+{
+    put_reg(PP_SelfCtl, PP_SelfCtl_Reset);  // Reset chip
+    while ((get_reg(PP_SelfStat) & PP_SelfStat_InitD) == 0) ;  
+}
+
+//
+// This function is called to "start up" the interface.  It may be called
+// multiple times, even when the hardware is already running.  It will be
+// called whenever something "hardware oriented" changes and should leave
+// the hardware ready to send/receive packets.
+//
+static void
+cs8900_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
+{
+    unsigned short stat;
+    put_reg(PP_BusCtl, PP_BusCtl_MemoryE);  // Disable interrupts, memory mode
+    put_reg(PP_IntReg, PP_IntReg_IRQ0);  // Only possibility
+    put_reg(PP_RxCFG, PP_RxCFG_RxOK | PP_RxCFG_CRC | 
+                      PP_RxCFG_RUNT | PP_RxCFG_EXTRA);
+    put_reg(PP_RxCTL, PP_RxCTL_RxOK | PP_RxCTL_Broadcast | 
+                      PP_RxCTL_IA);
+    put_reg(PP_TxCFG, PP_TxCFG_TxOK | PP_TxCFG_Collision | 
+                      PP_TxCFG_CRS | PP_TxCFG_SQE | PP_TxCFG_Late | 
+                      PP_TxCFG_Jabber | PP_TxCFG_16Collisions);
+    put_reg(PP_BufCFG, PP_BufCFG_TxRDY | PP_BufCFG_TxUE | PP_BufCFG_RxMiss | 
+                       PP_BufCFG_TxCol | PP_BufCFG_Miss | PP_BufCFG_SWI);
+    put_reg(PP_IntReg, PP_IntReg_IRQ0);  // Only possibility
+    put_reg(PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx);
+    // Clear Interrupt Status Queue before enabling interrupts
+    while ((stat = CS8900_ISQ) != 0) ;
+    put_reg(PP_BusCtl, PP_BusCtl_EnableIRQ);
+}
+
+//
+// This routine is called to perform special "control" opertions
+//
+static int
+cs8900_control(struct eth_drv_sc *sc, unsigned long key,
+               void *data, int data_length)
+{
+    switch (key) {
+    case ETH_DRV_SET_MAC_ADDRESS:
+        return 0;
+        break;
+    default:
+        return 1;
+        break;
+    }
+}
+
+//
+// This routine is called to see if it is possible to send another packet.
+// It will return non-zero if a transmit is possible, zero otherwise.
+//
+static int
+cs8900_can_send(struct eth_drv_sc *sc)
+{
+    struct cs8900_priv_data *cpd = (struct cs8900_priv_data *)sc->driver_private;
+    return (cpd->txbusy == 0);
+}
+
+//
+// This routine is called to send data to the hardware.
+static void 
+cs8900_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, 
+            int total_len, unsigned long key)
+{
+    struct cs8900_priv_data *cpd = (struct cs8900_priv_data *)sc->driver_private;
+    int i;
+    int len;
+    unsigned char *data;
+    unsigned short saved_data = 0, *sdata;
+    unsigned short stat;
+    bool odd_byte = false;
+
+    // Mark xmitter busy
+    cpd->txbusy = 1;
+    cpd->txkey = key;
+    // Start the xmit sequence
+// Note: this can go back once the 'dump' is removed
+//    CS8900_TxCMD = PP_TxCmd_TxStart_5;  // Start more-or-less immediately
+    CS8900_TxCMD = PP_TxCmd_TxStart_Full;  // Start only when all data sent to chip
+    CS8900_TxLEN = total_len;
+    stat = get_reg(PP_BusStat);  // This actually starts the xmit
+
+    // Put data into buffer
+    for (i = 0;  i < sg_len;  i++) {
+        data = (unsigned char *)sg_list[i].buf;
+        len = sg_list[i].len;
+        if (len > 0) {
+            /* Finish the last word. */
+            if (odd_byte) {
+                saved_data |= (*data++ << 8);
+                CS8900_RTDATA = saved_data;
+                len--;
+                odd_byte = false;
+            }
+            /* Output contiguous words. */
+            sdata = (unsigned short *)data;
+            while (len > 1) {
+                CS8900_RTDATA = *sdata++;
+                len -= sizeof(unsigned short);
+            }
+            /* Save last byte, if necessary. */
+            if (len == 1) {
+                data = (unsigned char *)sdata;
+                saved_data = *data;
+                odd_byte = true;
+            }
+        }
+    }
+    if (odd_byte) {
+        CS8900_RTDATA = saved_data;
+    }
+}
+
+//
+// This function is called when a packet has been received.  It's job is
+// to prepare to unload the packet from the hardware.  Once the length of
+// the packet is known, the upper layer of the driver can be told.  When
+// the upper layer is ready to unload the packet, the internal function
+// 'cs8900_recv' will be called to actually fetch it from the hardware.
+//
+static void
+cs8900_RxEvent(struct eth_drv_sc *sc)
+{
+    unsigned short stat, len;
+
+    stat = CS8900_RTDATA;
+    len = CS8900_RTDATA;
+    if (net_debug) {
+        diag_printf("RxEvent - stat: %x, len: %d\n", stat, len);
+    }
+    eth_drv_recv(sc, len);
+}
+
+//
+// This function is called as a result of the "eth_drv_recv()" call above.
+// It's job is to actually fetch data for a packet from the hardware once
+// memory buffers have been allocated for the packet.  Note that the buffers
+// may come in pieces, using a scatter-gather list.  This allows for more
+// efficient processing in the upper layers of the stack.
+//
+static void
+cs8900_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
+{
+    int i, mlen;
+    unsigned short *data, val;
+    unsigned char *cp, cval;
+
+    for (i = 0;  i < sg_len;  i++) {
+        data = (unsigned short *)sg_list[i].buf;
+        mlen = sg_list[i].len;
+        while (mlen >= sizeof(*data)) {
+            val = CS8900_RTDATA;
+            if (data) {
+                *data++ = val;
+            }
+            mlen -= sizeof(*data);
+        }
+        if (mlen) {
+            // Fetch last odd byte
+            cval = CS8900_RTDATA & 0xFF;
+            if (cp = (unsigned char *)data) {
+                *cp = cval;
+            }
+        }
+    }
+}
+
+static void
+cs8900_TxEvent(struct eth_drv_sc *sc, int stat)
+{
+    struct cs8900_priv_data *cpd = (struct cs8900_priv_data *)sc->driver_private;
+    stat = get_reg(PP_TER);
+    if (net_debug) {
+        diag_printf("Tx event: %x\n", stat);
+    }
+    cpd->txbusy = 0;
+    eth_drv_tx_done(sc, cpd->txkey, 0);
+}
+
+static void
+cs8900_BufEvent(struct eth_drv_sc *sc, int stat)
+{
+    if (stat & PP_BufCFG_RxMiss) {
+    }
+    if (stat & PP_BufCFG_TxUE) {
+    }
+}
+
+static void
+cs8900_int(struct eth_drv_sc *sc)
+{
+    unsigned short event;
+    while ((event = CS8900_ISQ) != 0) {
+        switch (event & ISQ_EventMask) {
+        case ISQ_RxEvent:
+            cs8900_RxEvent(sc);
+            break;
+        case ISQ_TxEvent:
+            cs8900_TxEvent(sc, event);
+            break;
+        case ISQ_BufEvent:
+            cs8900_BufEvent(sc, event);
+            break;
+        case ISQ_RxMissEvent:
+            // Receive miss counter has overflowed
+            break;
+        case ISQ_TxColEvent:
+            // Transmit collision counter has overflowed
+            break;
+        default:
+            diag_printf("%s: Unknown event: %x\n", __FUNCTION__, event);
+            break;
+        }
+    }
+}
+
+#ifdef INTS_DONT_WORK
+void
+cs8900_fake_int(cyg_addrword_t param)
+{
+    int s;
+    while (true) {
+        cyg_thread_delay(5);
+        s = splnet();
+        cs8900_int(&edb7xxx_sc);
+        splx(s);
+    }
+}
+#endif
+