Mercurial > ecos-v3_0-branch
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 +
