URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/ecos-2.0/packages/devs/eth/smsc
- from Rev 1254 to Rev 1765
- ↔ Reverse comparison
Rev 1254 → Rev 1765
/lan91cxx/v2_0/cdl/smsc_lan91cxx_eth_drivers.cdl
0,0 → 1,113
# ==================================================================== |
# |
# smsc_lan91cxx_eth_drivers.cdl |
# |
# Ethernet drivers - support for LAN91CXX compatible ethernet controllers |
# |
# ==================================================================== |
#####ECOSGPLCOPYRIGHTBEGIN#### |
## ------------------------------------------- |
## This file is part of eCos, the Embedded Configurable Operating System. |
## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. |
## |
## eCos is free software; you can redistribute it and/or modify it under |
## the terms of the GNU General Public License as published by the Free |
## Software Foundation; either version 2 or (at your option) any later version. |
## |
## eCos is distributed in the hope that it will be useful, but WITHOUT ANY |
## WARRANTY; without even the implied warranty of MERCHANTABILITY or |
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
## for more details. |
## |
## You should have received a copy of the GNU General Public License along |
## with eCos; if not, write to the Free Software Foundation, Inc., |
## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
## |
## As a special exception, if other files instantiate templates or use macros |
## or inline functions from this file, or you compile this file and link it |
## with other works to produce a work based on this file, this file does not |
## by itself cause the resulting work to be covered by the GNU General Public |
## License. However the source code for this file must still be made available |
## in accordance with section (3) of the GNU General Public License. |
## |
## This exception does not invalidate any other reasons why a work based on |
## this file might be covered by the GNU General Public License. |
## |
## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. |
## at http://sources.redhat.com/ecos/ecos-license/ |
## ------------------------------------------- |
#####ECOSGPLCOPYRIGHTEND#### |
# ==================================================================== |
######DESCRIPTIONBEGIN#### |
# |
# Author(s): gthomas |
# Contributors: gthomas, jskov |
# Date: 2000-11-22 |
# |
#####DESCRIPTIONEND#### |
# |
# ==================================================================== |
|
cdl_package CYGPKG_DEVS_ETH_SMSC_LAN91CXX { |
display "SMSC LAN91CXX compatible ethernet driver" |
description "Ethernet driver for SMSC LAN91CXX compatible controllers." |
|
parent CYGPKG_IO_ETH_DRIVERS |
active_if CYGPKG_IO_ETH_DRIVERS |
|
active_if CYGINT_DEVS_ETH_SMSC_LAN91CXX_REQUIRED |
|
include_dir . |
include_files ; # none _exported_ whatsoever |
compile -library=libextras.a if_lan91cxx.c |
|
define_proc { |
puts $::cdl_header "#include <pkgconf/system.h>"; |
puts $::cdl_header "#include CYGDAT_DEVS_ETH_SMSC_LAN91CXX_CFG"; |
} |
|
cdl_option CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM { |
display "SIOCSIFHWADDR records ESA (MAC address) in EEPROM" |
default_value 0 |
description " |
The ioctl() socket call with operand SIOCSIFHWADDR sets the |
interface hardware address - the MAC address or Ethernet Station |
Address (ESA). This option causes the new MAC address to be written |
into the EEPROM associated with the interface, so that the new |
MAC address is permanently recorded. Doing this should be a |
carefully chosen decision, hence this option." |
} |
|
cdl_interface CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA { |
display "ESA is statically configured" |
description " |
If this is nonzero, then the ESA (MAC address) is statically |
configured in the platform-specific package which instantiates |
this driver with all its details." |
} |
|
cdl_interface CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE { |
display "Chip is wired in PCMCIA mode" |
description " |
If this is nonzero, then the chip is assumed to be hardware |
configured in PCMCIA mode." |
} |
|
cdl_component CYGPKG_DEVS_ETH_SMSC_LAN91CXX_OPTIONS { |
display "LAN91CXX ethernet driver build options" |
flavor none |
no_define |
|
cdl_option CYGPKG_DEVS_ETH_SMSC_LAN91CXX_CFLAGS_ADD { |
display "Additional compiler flags" |
flavor data |
no_define |
default_value { "-D_KERNEL -D__ECOS" } |
description " |
This option modifies the set of compiler flags for |
building the LAN91CXX ethernet driver package. |
These flags are used in addition |
to the set of global flags." |
} |
} |
} |
/lan91cxx/v2_0/ChangeLog
0,0 → 1,158
2002-08-28 Mark Salter <msalter@redhat.com> |
|
* src/smsc_lan91cxx.h: Support 32-bit data reads. |
* src/if_lan91cxx.c: Fix standalone (RedBoot) interrupt handling. |
Support 32-bit data reads. |
|
2002-08-16 Mark Salter <msalter@redhat.com> |
|
* src/if_lan91cxx.c: Add support for 91C111. Platform-specific |
include file is now included from within smsc_lan91cxx.h so |
that register access functions may be overridden if necessary. |
* src/smsc_lan91cxx.h: Ditto. |
|
2002-06-14 Gary Thomas <gary@chez-thomas.org> |
|
* src/if_lan91cxx.c: |
Need to include <pkgconf/io_eth_drivers.h> for proper configuration |
of stand-alone (polled) vs. system (interrupt driven) mode. |
|
2001-12-12 Hugo Tyson <hmt@redhat.com> |
|
* src/if_lan91cxx.c (lan91cxx_recv): If discarding data due to |
caller out of buffers, we must read-out and discard the packet to |
correctly complete the transaction. Bogus assert for buffer |
pointer removed; other asserts from not discarding data assuaged |
by first change above. |
|
2001-08-17 Hugo Tyson <hmt@redhat.com> |
|
* src/if_lan91cxx.c (lan91cxx_poll): The interrupt acknowledge |
call only occurs in the ISR for this driver because the interrupt |
via GPIO is edge triggered. We now also acknowledge the interrupt |
within the poll() routine - otherwise RedBoot net use never acks! |
Which doesn't matter if the app uses the net, but in a net-free |
app, it near enough wedges in the resulting interrupt loop. |
|
2001-08-13 Hugo Tyson <hmt@redhat.com> |
|
* src/smsc_lan91cxx.h (get_att,put_att): Condition out the inline |
functions if not CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE - |
because they don't compile. Inlines are not like macros. |
|
2001-07-27 Jordi Colomer <jco@ict.es> |
|
* cdl/smsc_lan91cxx_eth_drivers.cdl: |
Add interface for whether the hardware is in PCMCIA mode. |
* src/if_lan91cxx.c (smsc_lan91cxx_init): |
Allow for hardware shift addresses |
Initialize appropriately if PCMCIA mode. |
* src/smsc_lan91cxx.h: |
Support address shifts in get_reg,put_Reg, get_data, put_data |
Add new functions get_banksel , put_att and get_att. |
|
2001-07-11 Hugo Tyson <hmt@redhat.com> |
|
* src/if_lan91cxx.c (lan91cxx_stop): Clean up any pending tx both |
to prevent mbuf leak due to noncompletion, and to clear tx_busy so |
that the newly upbrung device can be used. |
(lan91cxx_can_send): Similarly detect a stopped tx engine and |
restart it, and clean up any pending tx, in here. Otherwise the |
system cannot progress. |
|
2001-03-27 Hugo Tyson <hmt@redhat.com> |
2001-03-27 Robin Farine <acnrf@dial.eunet.ch> |
|
* src/if_lan91cxx.c (lan91cxx_start): Strip the CRC from |
incoming frames. |
|
2001-01-26 Hugo Tyson <hmt@redhat.com> |
|
* src/smsc_lan91cxx.h (smsc_lan91cxx_stats): Add stats structure. |
|
* src/if_lan91cxx.c (lan91cxx_control): Implement ioctl() call for |
SNMP statistics gathering. Implement stats counting generally. |
|
2001-01-25 Hugo Tyson <hmt@redhat.com> |
|
* cdl/smsc_lan91cxx_eth_drivers.cdl |
(CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM): New option to |
control whether the IOCTL to set the ESA actually writes the |
EEPROM. Default disabled for safety. |
(CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA): New interface controls |
whether a statically configured ESA is picked up from private data. |
|
* src/smsc_lan91cxx.h (LAN91CXX_CONFIG): Add this, plus |
(LAN91CXX_CONTROL_EEPROM_SELECT): et al for EEPROM writing. |
|
* src/if_lan91cxx.c (lan91cxx_control): Implement updating the |
EEPROM with the new ESA, if so configured. Otherwise just set the |
ESA in the device and continue, using the new value. |
(smsc_lan91cxx_init): Obey the STATIC_ESA interface setting to use the |
EEPROM or device data for the ESA. |
|
Removed some never-compiled, never-used stuff for getting an ESA |
out of RedBoot's flash storage. |
|
2001-01-25 Hugo Tyson <hmt@redhat.com> |
|
* cdl/smsc_lan91cxx_eth_drivers.cdl: This generic driver does not |
implement CYGHWR_NET_DRIVER_ETH0 et al; the instantiating package |
does. |
|
2001-01-24 Hugo Tyson <hmt@redhat.com> |
|
* src/if_lan91cxx.c (lan91cxx_start): Implement promiscuous mode. |
This just involves setting a bit in ..._start() if certain |
interface flags are set. Also import a couple of other details |
under #ifdef CYGPKG_NET from the i82559 driver for safety. |
|
2001-01-24 Hugo Tyson <hmt@redhat.com> |
|
* src/if_lan91cxx.c (DEBUG): Refined debug output, for trying it |
in RedBoot experiments. |
|
2001-01-22 Hugo Tyson <hmt@redhat.com> |
|
* src/smsc_lan91cxx.h: New file. |
* src/if_lan91cxx.c: New file. |
* cdl/smsc_lan91cxx_eth_drivers.cdl: New file. |
New component. Based on previous work [mis]named, in the same |
idiom, lan900. Hence this new component with a better name. A |
major rewrite too, to make the new one work with LAN91C96, |
specifically. |
|
//=========================================================================== |
//####ECOSGPLCOPYRIGHTBEGIN#### |
// ------------------------------------------- |
// This file is part of eCos, the Embedded Configurable Operating System. |
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. |
// |
// eCos is free software; you can redistribute it and/or modify it under |
// the terms of the GNU General Public License as published by the Free |
// Software Foundation; either version 2 or (at your option) any later version. |
// |
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY |
// WARRANTY; without even the implied warranty of MERCHANTABILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with eCos; if not, write to the Free Software Foundation, Inc., |
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
// |
// As a special exception, if other files instantiate templates or use macros |
// or inline functions from this file, or you compile this file and link it |
// with other works to produce a work based on this file, this file does not |
// by itself cause the resulting work to be covered by the GNU General Public |
// License. However the source code for this file must still be made available |
// in accordance with section (3) of the GNU General Public License. |
// |
// This exception does not invalidate any other reasons why a work based on |
// this file might be covered by the GNU General Public License. |
// |
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. |
// at http://sources.redhat.com/ecos/ecos-license/ |
// ------------------------------------------- |
//####ECOSGPLCOPYRIGHTEND#### |
//=========================================================================== |
/lan91cxx/v2_0/src/smsc_lan91cxx.h
0,0 → 1,460
#ifndef CYGONCE_DEVS_ETH_SMSC_LAN91CXX_LAN91CXX_H |
#define CYGONCE_DEVS_ETH_SMSC_LAN91CXX_LAN91CXX_H |
//========================================================================== |
// |
// lan91cxx.h |
// |
// SMCS LAN91C110 (LAN91CXX compatible) Ethernet chip |
// |
//========================================================================== |
//####ECOSGPLCOPYRIGHTBEGIN#### |
// ------------------------------------------- |
// This file is part of eCos, the Embedded Configurable Operating System. |
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. |
// |
// eCos is free software; you can redistribute it and/or modify it under |
// the terms of the GNU General Public License as published by the Free |
// Software Foundation; either version 2 or (at your option) any later version. |
// |
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY |
// WARRANTY; without even the implied warranty of MERCHANTABILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with eCos; if not, write to the Free Software Foundation, Inc., |
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
// |
// As a special exception, if other files instantiate templates or use macros |
// or inline functions from this file, or you compile this file and link it |
// with other works to produce a work based on this file, this file does not |
// by itself cause the resulting work to be covered by the GNU General Public |
// License. However the source code for this file must still be made available |
// in accordance with section (3) of the GNU General Public License. |
// |
// This exception does not invalidate any other reasons why a work based on |
// this file might be covered by the GNU General Public License. |
// |
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. |
// at http://sources.redhat.com/ecos/ecos-license/ |
// ------------------------------------------- |
//####ECOSGPLCOPYRIGHTEND#### |
//####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): jskov |
// Contributors: jskov, hmt, jco |
// Date: 2001-01-22 |
// Purpose: Hardware description of LAN9000 series, LAN91C96/110. |
// Description: |
// |
//####DESCRIPTIONEND#### |
// |
//========================================================================== |
|
#include <cyg/hal/hal_io.h> |
|
|
#define LAN91CXX_TCR 0x00 |
#define LAN91CXX_EPH_STATUS 0x01 |
#define LAN91CXX_RCR 0x02 |
#define LAN91CXX_COUNTER 0x03 |
#define LAN91CXX_MIR 0x04 |
#define LAN91CXX_MCR 0x05 // Other than 91C111 |
#define LAN91CXX_RPCR 0x05 // 91C111 only |
#define LAN91CXX_RESERVED_0 0x06 |
#define LAN91CXX_BS 0x07 |
#define LAN91CXX_CONFIG 0x08 |
#define LAN91CXX_BASE_REG 0x09 |
#define LAN91CXX_IA01 0x0a |
#define LAN91CXX_IA23 0x0b |
#define LAN91CXX_IA45 0x0c |
#define LAN91CXX_GENERAL 0x0d // 91C96 - was "RESERVED_1" for others |
#define LAN91CXX_CONTROL 0x0e |
#define LAN91CXX_BS2 0x0f |
#define LAN91CXX_MMU_COMMAND 0x10 |
#define LAN91CXX_PNR 0x11 |
#define LAN91CXX_FIFO_PORTS 0x12 |
#define LAN91CXX_POINTER 0x13 |
#define LAN91CXX_DATA_HIGH 0x14 |
#define LAN91CXX_DATA 0x15 |
#define LAN91CXX_INTERRUPT 0x16 |
#define LAN91CXX_BS3 0x17 |
#define LAN91CXX_MT01 0x18 |
#define LAN91CXX_MT23 0x19 |
#define LAN91CXX_MT45 0x1a |
#define LAN91CXX_MT67 0x1b |
#define LAN91CXX_MGMT 0x1c |
#define LAN91CXX_REVISION 0x1d |
#define LAN91CXX_ERCV 0x1e |
#define LAN91CXX_BS4 0x1f |
|
#define LAN91CXX_RCR_SOFT_RST 0x8000 // soft reset |
#define LAN91CXX_RCR_FILT_CAR 0x4000 // filter carrier |
#define LAN91CXX_RCR_ABORT_ENB 0x2000 // abort on collision |
#define LAN91CXX_RCR_STRIP_CRC 0x0200 // strip CRC |
#define LAN91CXX_RCR_RXEN 0x0100 // enable RX |
#define LAN91CXX_RCR_ALMUL 0x0004 // receive all muticasts |
#define LAN91CXX_RCR_PRMS 0x0002 // promiscuous |
#define LAN91CXX_RCR_RX_ABORT 0x0001 // set when abort due to long frame |
|
#define LAN91CXX_TCR_SWFDUP 0x8000 // Switched Full Duplex mode |
#define LAN91CXX_TCR_ETEN_TYPE 0x4000 // ETEN type (91C96) 0 <=> like a 91C94 |
#define LAN91CXX_TCR_EPH_LOOP 0x2000 // loopback mode |
#define LAN91CXX_TCR_STP_SQET 0x1000 // Stop transmission on SQET error |
#define LAN91CXX_TCR_FDUPLX 0x0800 // full duplex |
#define LAN91CXX_TCR_MON_CSN 0x0400 // monitor carrier during tx (91C96) |
#define LAN91CXX_TCR_NOCRC 0x0100 // does not append CRC to frames |
#define LAN91CXX_TCR_PAD_EN 0x0080 // pads frames with 00 to min length |
#define LAN91CXX_TCR_FORCOL 0x0004 // force collision |
#define LAN91CXX_TCR_LLOOP 0x0002 // local loopback (91C96) |
#define LAN91CXX_TCR_TXENA 0x0001 // enable |
|
#define LAN91CXX_POINTER_RCV 0x8000 |
#define LAN91CXX_POINTER_AUTO_INCR 0x4000 |
#define LAN91CXX_POINTER_READ 0x2000 |
#define LAN91CXX_POINTER_ETEN 0x1000 |
#define LAN91CXX_POINTER_NOT_EMPTY 0x0800 |
|
|
#define LAN91CXX_INTERRUPT_TX_IDLE_M 0x8000 // (91C96) |
#define LAN91CXX_INTERRUPT_ERCV_INT_M 0x4000 |
#define LAN91CXX_INTERRUPT_EPH_INT_M 0x2000 |
#define LAN91CXX_INTERRUPT_RX_OVRN_INT_M 0x1000 |
#define LAN91CXX_INTERRUPT_ALLOC_INT_M 0x0800 |
#define LAN91CXX_INTERRUPT_TX_EMPTY_INT_M 0x0400 |
#define LAN91CXX_INTERRUPT_TX_INT_M 0x0200 |
#define LAN91CXX_INTERRUPT_RCV_INT_M 0x0100 |
#define LAN91CXX_INTERRUPT_TX_IDLE 0x0080 // (91C96) |
#define LAN91CXX_INTERRUPT_ERCV_INT 0x0040 // also ack |
#define LAN91CXX_INTERRUPT_EPH_INT 0x0020 |
#define LAN91CXX_INTERRUPT_RX_OVRN_INT 0x0010 // also ack |
#define LAN91CXX_INTERRUPT_ALLOC_INT 0x0008 |
#define LAN91CXX_INTERRUPT_TX_EMPTY_INT 0x0004 // also ack |
#define LAN91CXX_INTERRUPT_TX_INT 0x0002 // also ack |
#define LAN91CXX_INTERRUPT_RCV_INT 0x0001 |
|
#if 0 // Whichever we choose, the behaviour is the same. |
#define LAN91CXX_INTERRUPT_TX_SET 0x0002 // TX |
#define LAN91CXX_INTERRUPT_TX_SET_ACK 0x0000 // -none- |
#define LAN91CXX_INTERRUPT_TX_FIFO_ACK 0x0002 // TX alone |
#define LAN91CXX_INTERRUPT_TX_SET_M 0x0200 // TX alone |
#else |
#define LAN91CXX_INTERRUPT_TX_SET 0x0006 // TX_EMPTY + TX |
#define LAN91CXX_INTERRUPT_TX_SET_ACK 0x0004 // TX_EMPTY and not plain TX |
#define LAN91CXX_INTERRUPT_TX_FIFO_ACK 0x0002 // TX alone |
#define LAN91CXX_INTERRUPT_TX_SET_M 0x0600 // TX_EMPTY + TX |
#endif |
|
#define LAN91CXX_CONTROL_RCV_BAD 0x4000 |
#define LAN91CXX_CONTROL_AUTO_RELEASE 0x0800 |
#define LAN91CXX_CONTROL_LE_ENABLE 0x0080 |
#define LAN91CXX_CONTROL_CR_ENABLE 0x0040 |
#define LAN91CXX_CONTROL_TE_ENABLE 0x0020 |
|
// These are for setting the MAC address in the 91C96 serial EEPROM |
#define LAN91CXX_CONTROL_EEPROM_SELECT 0x0004 |
#define LAN91CXX_CONTROL_RELOAD 0x0002 |
#define LAN91CXX_CONTROL_STORE 0x0001 |
#define LAN91CXX_CONTROL_EEPROM_BUSY 0x0003 |
#define LAN91CXX_ESA_EEPROM_OFFSET 0x0020 |
|
#define LAN91CXX_STATUS_TX_UNRN 0x8000 |
#define LAN91CXX_STATUS_LINK_OK 0x4000 |
#define LAN91CXX_STATUS_CTR_ROL 0x1000 |
#define LAN91CXX_STATUS_EXC_DEF 0x0800 |
#define LAN91CXX_STATUS_LOST_CARR 0x0400 |
#define LAN91CXX_STATUS_LATCOL 0x0200 |
#define LAN91CXX_STATUS_WAKEUP 0x0100 |
#define LAN91CXX_STATUS_TX_DEFR 0x0080 |
#define LAN91CXX_STATUS_LTX_BRD 0x0040 |
#define LAN91CXX_STATUS_SQET 0x0020 |
#define LAN91CXX_STATUS_16COL 0x0010 |
#define LAN91CXX_STATUS_LTX_MULT 0x0008 |
#define LAN91CXX_STATUS_MUL_COL 0x0004 |
#define LAN91CXX_STATUS_SNGL_COL 0x0002 |
#define LAN91CXX_STATUS_TX_SUC 0x0001 |
|
#define LAN91CXX_MMU_noop 0x0000 |
#define LAN91CXX_MMU_alloc_for_tx 0x0020 |
#define LAN91CXX_MMU_reset_mmu 0x0040 |
#define LAN91CXX_MMU_rem_rx_frame 0x0060 |
#define LAN91CXX_MMU_rem_tx_frame 0x0070 // (91C96) only when TX stopped |
#define LAN91CXX_MMU_remrel_rx_frame 0x0080 |
#define LAN91CXX_MMU_rel_packet 0x00a0 |
#define LAN91CXX_MMU_enq_packet 0x00c0 |
#define LAN91CXX_MMU_reset_tx_fifo 0x00e0 |
|
#define LAN91CXX_CONTROLBYTE_CRC 0x1000 |
#define LAN91CXX_CONTROLBYTE_ODD 0x2000 |
#define LAN91CXX_CONTROLBYTE_RX 0x4000 |
|
#define LAN91CXX_RX_STATUS_ALIGNERR 0x8000 |
#define LAN91CXX_RX_STATUS_BCAST 0x4000 |
#define LAN91CXX_RX_STATUS_BADCRC 0x2000 |
#define LAN91CXX_RX_STATUS_ODDFRM 0x1000 |
#define LAN91CXX_RX_STATUS_TOOLONG 0x0800 |
#define LAN91CXX_RX_STATUS_TOOSHORT 0x0400 |
#define LAN91CXX_RX_STATUS_HASHVALMASK 0x007e // MASK |
#define LAN91CXX_RX_STATUS_MCAST 0x0001 |
#define LAN91CXX_RX_STATUS_BAD \ |
(LAN91CXX_RX_STATUS_ALIGNERR | \ |
LAN91CXX_RX_STATUS_BADCRC | \ |
LAN91CXX_RX_STATUS_TOOLONG | \ |
LAN91CXX_RX_STATUS_TOOSHORT) |
|
// Attribute memory registers in PCMCIA mode |
#define LAN91CXX_ECOR 0x8000 |
#define LAN91CXX_ECOR_RESET (1<<7) |
#define LAN91CXX_ECOR_LEVIRQ (1<<6) |
#define LAN91CXX_ECOR_ATTWR (1<<2) |
#define LAN91CXX_ECOR_ENABLE (1<<0) |
|
#define LAN91CXX_ECSR 0x8002 |
#define LAN91CXX_ECSR_IOIS8 (1<<5) |
#define LAN91CXX_ECSR_PWRDWN (1<<2) |
#define LAN91CXX_ECSR_INTR (1<<1) |
|
// These are for manipulating the MII interface |
#define LAN91CXX_MGMT_MDO 0x0001 |
#define LAN91CXX_MGMT_MDI 0x0002 |
#define LAN91CXX_MGMT_MCLK 0x0004 |
#define LAN91CXX_MGMT_MDOE 0x0008 |
|
// Internal PHY registers (91c111) |
#define LAN91CXX_PHY_CTRL 0 |
#define LAN91CXX_PHY_STAT 1 |
#define LAN91CXX_PHY_ID1 2 |
#define LAN91CXX_PHY_ID2 3 |
#define LAN91CXX_PHY_AUTO_AD 4 |
#define LAN91CXX_PHY_AUTO_CAP 5 |
#define LAN91CXX_PHY_CONFIG1 16 |
#define LAN91CXX_PHY_CONFIG2 17 |
#define LAN91CXX_PHY_STATUS_OUT 18 |
#define LAN91CXX_PHY_MASK 19 |
|
// PHY control bits |
#define LAN91CXX_PHY_CTRL_COLTST (1 << 7) |
#define LAN91CXX_PHY_CTRL_DPLX (1 << 8) |
#define LAN91CXX_PHY_CTRL_ANEG_RST (1 << 9) |
#define LAN91CXX_PHY_CTRL_MII_DIS (1 << 10) |
#define LAN91CXX_PHY_CTRL_PDN (1 << 11) |
#define LAN91CXX_PHY_CTRL_ANEG_EN (1 << 12) |
#define LAN91CXX_PHY_CTRL_SPEED (1 << 13) |
#define LAN91CXX_PHY_CTRL_LPBK (1 << 14) |
#define LAN91CXX_PHY_CTRL_RST (1 << 15) |
|
#define LAN91CXX_RPCR_LEDA_LINK (0 << 2) |
#define LAN91CXX_RPCR_LEDA_TXRX (4 << 2) |
#define LAN91CXX_RPCR_LEDA_RX (6 << 2) |
#define LAN91CXX_RPCR_LEDA_TX (7 << 2) |
#define LAN91CXX_RPCR_LEDB_LINK (0 << 5) |
#define LAN91CXX_RPCR_LEDB_TXRX (4 << 5) |
#define LAN91CXX_RPCR_LEDB_RX (6 << 5) |
#define LAN91CXX_RPCR_LEDB_TX (7 << 5) |
#define LAN91CXX_RPCR_ANEG (1 << 11) |
#define LAN91CXX_RPCR_DPLX (1 << 12) |
#define LAN91CXX_RPCR_SPEED (1 << 13) |
|
|
// ------------------------------------------------------------------------ |
|
#ifdef KEEP_STATISTICS |
struct smsc_lan91cxx_stats { |
unsigned int tx_good ; |
unsigned int tx_max_collisions ; |
unsigned int tx_late_collisions ; |
unsigned int tx_underrun ; |
unsigned int tx_carrier_loss ; |
unsigned int tx_deferred ; |
unsigned int tx_sqetesterrors ; |
unsigned int tx_single_collisions; |
unsigned int tx_mult_collisions ; |
unsigned int tx_total_collisions ; |
unsigned int rx_good ; |
unsigned int rx_crc_errors ; |
unsigned int rx_align_errors ; |
unsigned int rx_resource_errors ; |
unsigned int rx_overrun_errors ; |
unsigned int rx_collisions ; |
unsigned int rx_short_frames ; |
unsigned int rx_too_long_frames ; |
unsigned int rx_symbol_errors ; |
unsigned int interrupts ; |
unsigned int rx_count ; |
unsigned int rx_deliver ; |
unsigned int rx_resource ; |
unsigned int rx_restart ; |
unsigned int tx_count ; |
unsigned int tx_complete ; |
unsigned int tx_dropped ; |
}; |
#endif |
|
typedef struct lan91cxx_priv_data { |
int txbusy; // A packet has been sent |
unsigned long txkey; // Used to ack when packet sent |
unsigned short* base; // Base I/O address of controller |
// (as it comes out of reset) |
#if CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE |
unsigned char* attbase; // Base attribute address of controller |
// only used in PCMCIA mode |
#endif |
int interrupt; // Interrupt vector used by controller |
unsigned char enaddr[6]; // Controller ESA |
// Function to configure the ESA - may fetch ESA from EPROM or |
// RedBoot config option. |
void (*config_enaddr)(struct lan91cxx_priv_data* cpd); |
int txpacket; |
int rxpacket; |
int within_send; |
int addrsh; // Address bits to shift |
#ifdef KEEP_STATISTICS |
struct smsc_lan91cxx_stats stats; |
#endif |
} lan91cxx_priv_data; |
|
// ------------------------------------------------------------------------ |
|
#include CYGDAT_DEVS_ETH_SMSC_LAN91CXX_INL |
|
#ifdef LAN91CXX_32BIT_RX |
typedef cyg_uint32 rxd_t; |
#else |
typedef cyg_uint16 rxd_t; |
#endif |
|
#ifndef SMSC_PLATFORM_DEFINED_GET_REG |
static __inline__ unsigned short |
get_reg(struct eth_drv_sc *sc, int regno) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
unsigned short val; |
|
HAL_WRITE_UINT16(cpd->base+(LAN91CXX_BS << cpd->addrsh), regno>>3); |
HAL_READ_UINT16(cpd->base+((regno&0x7) << cpd->addrsh), val); |
#if DEBUG & 2 |
diag_printf("read reg %d val 0x%04x\n", regno, val); |
#endif |
return val; |
} |
#endif // SMSC_PLATFORM_DEFINED_GET_REG |
|
#ifndef SMSC_PLATFORM_DEFINED_PUT_REG |
static __inline__ void |
put_reg(struct eth_drv_sc *sc, int regno, unsigned short val) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
|
HAL_WRITE_UINT16(cpd->base+(LAN91CXX_BS << cpd->addrsh), regno>>3); |
HAL_WRITE_UINT16(cpd->base+((regno&0x7) << cpd->addrsh), val); |
|
#if DEBUG & 2 |
diag_printf("write reg %d val 0x%04x\n", regno, val); |
#endif |
} |
#endif // SMSC_PLATFORM_DEFINED_PUT_REG |
|
#ifndef SMSC_PLATFORM_DEFINED_PUT_DATA |
// ------------------------------------------------------------------------ |
// Assumes bank2 has been selected |
static __inline__ void |
put_data(struct eth_drv_sc *sc, unsigned short val) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
|
HAL_WRITE_UINT16(cpd->base+((LAN91CXX_DATA & 0x7) << cpd->addrsh), val); |
|
#if DEBUG & 2 |
diag_printf("write data 0x%04x\n", val); |
#endif |
} |
#endif // SMSC_PLATFORM_DEFINED_PUT_DATA |
|
#ifndef SMSC_PLATFORM_DEFINED_GET_DATA |
// Assumes bank2 has been selected |
static __inline__ rxd_t |
get_data(struct eth_drv_sc *sc) |
{ |
rxd_t val; |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
|
#ifdef LAN91CXX_32BIT_RX |
HAL_READ_UINT32(cpd->base+((LAN91CXX_DATA_HIGH & 0x7) << cpd->addrsh), val); |
#else |
HAL_READ_UINT16(cpd->base+((LAN91CXX_DATA & 0x7) << cpd->addrsh), val); |
#endif |
|
#if DEBUG & 2 |
diag_printf("read data 0x%x\n", val); |
#endif |
return val; |
} |
#endif // SMSC_PLATFORM_DEFINED_GET_DATA |
|
// ------------------------------------------------------------------------ |
// Read the bank register (this one is bank-independent) |
static __inline__ unsigned short |
get_banksel(struct eth_drv_sc *sc) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
unsigned short val; |
|
HAL_READ_UINT16(cpd->base+(LAN91CXX_BS << cpd->addrsh), val); |
#if DEBUG & 2 |
diag_printf("read bank val 0x%04x\n", val); |
#endif |
return val; |
} |
|
|
// ------------------------------------------------------------------------ |
// Write on PCMCIA attribute memory |
#if CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE |
static __inline__ void |
put_att(struct eth_drv_sc *sc, int offs, unsigned char val) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
|
HAL_WRITE_UINT8(cpd->attbase + (offs << cpd->addrsh), val); |
|
#if DEBUG & 2 |
diag_printf("write attr %d val 0x%02x\n", offs, val); |
#endif |
} |
|
// Read from PCMCIA attribute memory |
static __inline__ unsigned char |
get_att(struct eth_drv_sc *sc, int offs) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
unsigned char val; |
|
HAL_READ_UINT8(cpd->attbase + (offs << cpd->addrsh), val); |
#if DEBUG & 2 |
diag_printf("read attr %d val 0x%02x\n", offs, val); |
#endif |
return val; |
} |
#endif // #if CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE |
|
// ------------------------------------------------------------------------ |
#endif // CYGONCE_DEVS_ETH_SMSC_LAN91CXX_LAN91CXX_H |
// EOF smsc_lan91cxx.h |
/lan91cxx/v2_0/src/if_lan91cxx.c
0,0 → 1,1305
//========================================================================== |
// |
// dev/if_lan91cxx.c |
// |
// Ethernet device driver for SMSC LAN91CXX compatible controllers |
// |
//========================================================================== |
//####ECOSGPLCOPYRIGHTBEGIN#### |
// ------------------------------------------- |
// This file is part of eCos, the Embedded Configurable Operating System. |
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. |
// |
// eCos is free software; you can redistribute it and/or modify it under |
// the terms of the GNU General Public License as published by the Free |
// Software Foundation; either version 2 or (at your option) any later version. |
// |
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY |
// WARRANTY; without even the implied warranty of MERCHANTABILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with eCos; if not, write to the Free Software Foundation, Inc., |
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
// |
// As a special exception, if other files instantiate templates or use macros |
// or inline functions from this file, or you compile this file and link it |
// with other works to produce a work based on this file, this file does not |
// by itself cause the resulting work to be covered by the GNU General Public |
// License. However the source code for this file must still be made available |
// in accordance with section (3) of the GNU General Public License. |
// |
// This exception does not invalidate any other reasons why a work based on |
// this file might be covered by the GNU General Public License. |
// |
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. |
// at http://sources.redhat.com/ecos/ecos-license/ |
// ------------------------------------------- |
//####ECOSGPLCOPYRIGHTEND#### |
//####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): hmt, based on lan900 (for LAN91C110) driver by jskov |
// jskov, based on CS8900 driver by Gary Thomas |
// Contributors: gthomas, jskov, hmt, jco@ict.es |
// Date: 2001-01-22 |
// Purpose: |
// Description: hardware driver for LAN91CXX "LAN9000" ethernet |
// Notes: Pointer register is not saved/restored on receive interrupts. |
// The pointer is shared by both receive/transmit code. |
// But the net stack manages atomicity for you here. |
// |
// The controller has an autorelease mode that allows TX packets |
// to be freed automatically on successful transmission - but |
// that is not used since we're only sending one packet at a |
// time anyway. |
// We may want to pingpong in future for throughput reasons. |
// |
// <jco@ict.es> Added support for PCMCIA mode and shifted |
// address buses. |
// |
//####DESCRIPTIONEND#### |
// |
//========================================================================== |
|
// Based on LAN91C110 and LAN91C96 |
|
#include <pkgconf/system.h> |
#include <pkgconf/devs_eth_smsc_lan91cxx.h> |
#include <pkgconf/io_eth_drivers.h> |
|
#include <cyg/infra/cyg_type.h> |
#include <cyg/hal/hal_arch.h> |
#include <cyg/hal/hal_intr.h> |
#include <cyg/hal/hal_diag.h> |
#include <cyg/infra/cyg_ass.h> |
#include <cyg/infra/diag.h> |
#include <cyg/hal/drv_api.h> |
#include <cyg/io/eth/netdev.h> |
#include <cyg/io/eth/eth_drv.h> |
#ifdef CYGPKG_NET |
#include <pkgconf/net.h> |
#include <cyg/kernel/kapi.h> |
#include <net/if.h> /* Needed for struct ifnet */ |
#endif |
|
#ifdef CYGPKG_INFRA_DEBUG |
// Then we log, OOI, the number of times we get a bad packet number |
// from the tx done fifo. |
int lan91cxx_txfifo_good = 0; |
int lan91cxx_txfifo_bad = 0; |
#endif |
|
// Set to perms of: |
// 0 disables all debug output |
// 1 for process debug output |
// 2 for added data IO output: get_reg, put_reg |
// 4 for packet allocation/free output |
// 8 for only startup status, so we can tell we're installed OK |
#define DEBUG 0 |
|
#if DEBUG & 1 |
#define DEBUG_FUNCTION() do { diag_printf("%s\n", __FUNCTION__); } while (0) |
#else |
#define DEBUG_FUNCTION() do {} while(0) |
#endif |
|
#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD) |
#define KEEP_STATISTICS |
#endif |
|
#ifdef KEEP_STATISTICS |
#define INCR_STAT( _x_ ) (cpd->stats. _x_ ++) |
#else |
#define INCR_STAT( _x_ ) CYG_EMPTY_STATEMENT |
#endif |
|
#include "smsc_lan91cxx.h" |
|
#ifdef LAN91CXX_IS_LAN91C111 |
static void lan91cxx_write_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr, |
cyg_uint8 phyreg, cyg_uint16 value); |
static cyg_uint16 lan91cxx_read_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr, |
cyg_uint8 phyreg); |
#endif |
|
static void lan91cxx_poll(struct eth_drv_sc *sc); |
|
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE |
static cyg_interrupt lan91cxx_interrupt; |
static cyg_handle_t lan91cxx_interrupt_handle; |
|
// This ISR is called when the ethernet interrupt occurs |
static int |
lan91cxx_isr(cyg_vector_t vector, cyg_addrword_t data |
/* , HAL_SavedRegisters *regs */ ) |
{ |
struct eth_drv_sc *sc = (struct eth_drv_sc *)data; |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
|
DEBUG_FUNCTION(); |
|
INCR_STAT( interrupts ); |
|
cyg_drv_interrupt_mask(cpd->interrupt); |
cyg_drv_interrupt_acknowledge(cpd->interrupt); |
return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR); // Run the DSR |
} |
#endif |
|
// The deliver function (ex-DSR) handles the ethernet [logical] processing |
static void |
lan91cxx_deliver(struct eth_drv_sc *sc) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
|
DEBUG_FUNCTION(); |
|
// Service the interrupt: |
lan91cxx_poll(sc); |
// Allow interrupts to happen again |
cyg_drv_interrupt_unmask(cpd->interrupt); |
} |
|
static int |
lan91cxx_int_vector(struct eth_drv_sc *sc) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
|
return (cpd->interrupt); |
} |
|
static bool |
smsc_lan91cxx_init(struct cyg_netdevtab_entry *tab) |
{ |
struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance; |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
unsigned short val; |
int i; |
#if CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE |
unsigned char ecor, ecsr; |
#endif |
|
DEBUG_FUNCTION(); |
|
cpd->txbusy = cpd->within_send = 0; |
|
#ifdef CYGNUM_DEVS_ETH_SMSC_LAN91CXX_SHIFT_ADDR |
cpd->addrsh = CYGNUM_DEVS_ETH_SMSC_LAN91CXX_SHIFT_ADDR; |
#else |
cpd->addrsh = 0; |
#endif |
|
#if CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE |
|
// If the chip is configured in PCMCIA mode, the internal |
// registers mapped in the attribute memory should be |
// initialized (i.e. to enable the I/O map) |
|
ecor = get_att(sc, LAN91CXX_ECOR); |
|
// pulse SRESET on ECOR |
ecor |= LAN91CXX_ECOR_RESET; |
put_att(sc, LAN91CXX_ECOR, ecor); |
|
HAL_DELAY_US(1); |
|
ecor &= ~LAN91CXX_ECOR_RESET; |
put_att(sc, LAN91CXX_ECOR, ecor); |
|
// then, enable I/O map |
ecor |= LAN91CXX_ECOR_ENABLE; |
put_att(sc, LAN91CXX_ECOR, ecor); |
|
// verify the register contents |
if (ecor != get_att(sc, LAN91CXX_ECOR)) |
diag_printf("LAN91CXX - Cannot access PCMCIA attribute registers\n"); |
|
ecsr = get_att(sc, LAN91CXX_ECSR); |
#ifdef CYGSEM_DEVS_ETH_SMSC_LAN91CXX_8_BIT |
#error "91CXX 8-bit mode not yet supported." |
ecsr |= LAN91CXX_ECSR_IOIS8; |
#else |
ecsr &= ~LAN91CXX_ECSR_IOIS8; |
#endif |
put_att(sc, LAN91CXX_ECSR, ecsr); |
|
#endif |
|
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE |
// Initialize environment, setup interrupt handler |
cyg_drv_interrupt_create(cpd->interrupt, |
99, // Priority - what goes here? |
(cyg_addrword_t)sc, // Data item passed to interrupt handler |
(cyg_ISR_t *)lan91cxx_isr, |
(cyg_DSR_t *)eth_drv_dsr, // The logical driver DSR |
&lan91cxx_interrupt_handle, |
&lan91cxx_interrupt); |
cyg_drv_interrupt_attach(lan91cxx_interrupt_handle); |
#endif // !CYGPKG_IO_ETH_DRIVERS_STAND_ALONE |
cyg_drv_interrupt_acknowledge(cpd->interrupt); |
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE |
cyg_drv_interrupt_unmask(cpd->interrupt); |
#endif // !CYGPKG_IO_ETH_DRIVERS_STAND_ALONE |
|
// probe chip by reading the signature in BS register |
val = get_banksel(sc); |
#if DEBUG & 9 |
diag_printf("LAN91CXX - supposed BankReg @ %x = %04x\n", |
cpd->base+LAN91CXX_BS, val ); |
#endif |
CYG_ASSERT( 0x3300 == (0xff00 & val), "No 91Cxx signature" ); |
|
val = get_reg(sc, LAN91CXX_REVISION); |
|
#if DEBUG & 9 |
diag_printf("LAN91CXX - type: %01x, rev: %01x\n", |
(val>>4)&0xf, val & 0xf); |
#endif |
|
// The controller may provide a function used to set up the ESA |
if (cpd->config_enaddr) |
(*cpd->config_enaddr)(cpd); |
|
// Reset chip |
put_reg(sc, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST); |
put_reg(sc, LAN91CXX_RCR, 0); |
|
val = get_reg(sc, LAN91CXX_EPH_STATUS); |
|
#if DEBUG & 9 |
diag_printf("LAN91CXX - status: %04x\n", val); |
#endif |
|
#if 0 < CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA |
// Use statically configured ESA from the private data |
#if DEBUG & 9 |
diag_printf("LAN91CXX - static ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", |
cpd->enaddr[0], |
cpd->enaddr[1], |
cpd->enaddr[2], |
cpd->enaddr[3], |
cpd->enaddr[4], |
cpd->enaddr[5] ); |
#endif // DEBUG |
// Set up hardware address |
for (i = 0; i < sizeof(cpd->enaddr); i += 2) |
put_reg(sc, LAN91CXX_IA01+i/2, |
cpd->enaddr[i] | (cpd->enaddr[i+1] << 8)); |
#else // not CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA |
// Use the address from the serial EEPROM |
|
// Read out hardware address |
for (i = 0; i < sizeof(cpd->enaddr); i += 2) { |
unsigned short z = get_reg(sc, LAN91CXX_IA01+i/2 ); |
cpd->enaddr[i] = (unsigned char)(0xff & z); |
cpd->enaddr[i+1] = (unsigned char)(0xff & (z >> 8)); |
} |
#if DEBUG & 9 |
diag_printf("LAN91CXX - eeprom ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", |
cpd->enaddr[0], |
cpd->enaddr[1], |
cpd->enaddr[2], |
cpd->enaddr[3], |
cpd->enaddr[4], |
cpd->enaddr[5] ); |
#endif // DEBUG |
#endif // !CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA |
|
// Initialize upper level driver |
(sc->funs->eth_drv->init)(sc, cpd->enaddr); |
return true; |
} |
|
static void |
lan91cxx_stop(struct eth_drv_sc *sc) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
DEBUG_FUNCTION(); |
|
CYG_ASSERT( cpd->within_send < 10, "stop: Excess send recursions" ); |
cpd->within_send++; |
// Complete any outstanding activity: |
if ( cpd->txbusy ) { |
cpd->txbusy = 0; |
#if DEBUG & 9 |
diag_printf("LAN91CXX - Stopping, cleaning up pending TX\n" ); |
#endif |
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0); |
} |
// Reset chip |
put_reg(sc, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST); |
put_reg(sc, LAN91CXX_RCR, 0); |
cpd->txbusy = cpd->within_send = 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 |
lan91cxx_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags) |
{ |
cyg_uint16 intr; |
#ifdef LAN91CXX_IS_LAN91C111 |
cyg_uint16 phy_ctl; |
int delay; |
#endif |
#ifdef CYGPKG_NET |
struct ifnet *ifp = &sc->sc_arpcom.ac_if; |
#endif |
DEBUG_FUNCTION(); |
|
#ifdef LAN91CXX_IS_LAN91C111 |
HAL_DELAY_US(100000); |
|
// 91C111 Errata. Internal PHY comes up disabled. Must enable here. |
phy_ctl = lan91cxx_read_phy(sc, 0, LAN91CXX_PHY_CTRL); |
phy_ctl &= ~LAN91CXX_PHY_CTRL_MII_DIS; |
lan91cxx_write_phy(sc, 0, LAN91CXX_PHY_CTRL, phy_ctl); |
|
// Start auto-negotiation |
put_reg(sc, LAN91CXX_RPCR, |
LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK | LAN91CXX_RPCR_ANEG); |
|
// wait for auto-negotiation to finish. |
// give it ~5 seconds before giving up (no cable?) |
delay = 50; |
while (!(lan91cxx_read_phy(sc, 0, LAN91CXX_PHY_STAT) & 0x20)) { |
if (--delay <= 0) |
break; |
HAL_DELAY_US(100000); |
} |
#endif |
|
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_reset_mmu); |
|
put_reg(sc, LAN91CXX_INTERRUPT, 0); // disable interrupts |
intr = get_reg(sc, LAN91CXX_INTERRUPT); |
put_reg(sc, LAN91CXX_INTERRUPT, intr & // ack old interrupts |
(LAN91CXX_INTERRUPT_TX_INT | LAN91CXX_INTERRUPT_TX_EMPTY_INT | |
LAN91CXX_INTERRUPT_RX_OVRN_INT | LAN91CXX_INTERRUPT_ERCV_INT)); |
put_reg(sc, LAN91CXX_RCR, |
#ifdef RCR_HAS_ABORT_ENB // 91C96 does not - page 46. |
LAN91CXX_RCR_ABORT_ENB | |
#endif |
LAN91CXX_RCR_STRIP_CRC | |
LAN91CXX_RCR_RXEN | LAN91CXX_RCR_ALMUL); |
put_reg(sc, LAN91CXX_TCR, LAN91CXX_TCR_TXENA | LAN91CXX_TCR_PAD_EN); |
put_reg(sc, LAN91CXX_CONTROL, 0); |
put_reg(sc, LAN91CXX_INTERRUPT, // enable interrupts |
LAN91CXX_INTERRUPT_RCV_INT_M); |
|
#ifdef CYGPKG_NET |
if (( 0 |
#ifdef ETH_DRV_FLAGS_PROMISC_MODE |
!= (flags & ETH_DRV_FLAGS_PROMISC_MODE) |
#endif |
) || (ifp->if_flags & IFF_PROMISC) |
) { |
// Then we select promiscuous mode. |
unsigned short rcr; |
rcr = get_reg(sc, LAN91CXX_RCR ); |
rcr |= LAN91CXX_RCR_PRMS; |
put_reg(sc, LAN91CXX_RCR, rcr ); |
} |
#endif |
} |
|
// |
// This routine is called to perform special "control" opertions |
// |
static int |
lan91cxx_control(struct eth_drv_sc *sc, unsigned long key, |
void *data, int data_length) |
{ |
unsigned char *esa = (unsigned char *)data; |
int i; |
unsigned short reg; |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
|
DEBUG_FUNCTION(); |
|
switch (key) { |
case ETH_DRV_SET_MAC_ADDRESS: |
#if 9 & DEBUG |
diag_printf("LAN91CXX - set ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", |
esa[0], |
esa[1], |
esa[2], |
esa[3], |
esa[4], |
esa[5] ); |
#ifndef CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM |
diag_printf("*** PERMANENT EEPROM WRITE NOT ENABLED ***\n"); |
#endif |
#endif // DEBUG |
|
#ifdef CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM |
// Only now can we command the chip to perform EEPROM writes: |
|
// select arbitrary writing to the EEPROM |
reg = get_reg(sc, LAN91CXX_CONTROL); |
reg |= LAN91CXX_CONTROL_EEPROM_SELECT; |
put_reg(sc, LAN91CXX_CONTROL, reg ); |
|
for (i = 0; i < sizeof(cpd->enaddr); i += 2) { |
int j; |
// Set the address register |
put_reg(sc, LAN91CXX_POINTER, LAN91CXX_ESA_EEPROM_OFFSET + i/2); |
// Poke the data |
put_reg(sc, LAN91CXX_GENERAL, esa[i] | (esa[i+1] << 8)); |
// Command the store |
reg = get_reg(sc, LAN91CXX_CONTROL); |
reg |= LAN91CXX_CONTROL_STORE; |
put_reg(sc, LAN91CXX_CONTROL, reg ); |
// and poll for completion |
for ( j = 1024 * 1024; 0 < j ; j-- ) { |
reg = get_reg(sc, LAN91CXX_CONTROL); |
if ( 0 == (reg & LAN91CXX_CONTROL_EEPROM_BUSY) ) |
break; |
} |
CYG_ASSERT( 0 < j, "EEPROM write timout!" ); |
} |
|
reg = get_reg(sc, LAN91CXX_CONTROL); |
CYG_ASSERT( 0 == (reg & LAN91CXX_CONTROL_EEPROM_BUSY), |
"EEPROM still busy!" ); |
// Clear the EEPROM selection bit |
reg &=~LAN91CXX_CONTROL_EEPROM_SELECT; |
put_reg(sc, LAN91CXX_CONTROL, reg ); |
// and check it "took" |
reg = get_reg(sc, LAN91CXX_CONTROL); |
CYG_ASSERT( 0 == (reg & LAN91CXX_CONTROL_EEPROM_SELECT), |
"EEPROM still selected!" ); |
// and command a complete reload |
reg |= LAN91CXX_CONTROL_RELOAD; |
put_reg(sc, LAN91CXX_CONTROL, reg ); |
for ( i = 1024 * 1024; 0 < i ; i-- ) { |
reg = get_reg(sc, LAN91CXX_CONTROL); |
if ( 0 == (reg & LAN91CXX_CONTROL_EEPROM_BUSY) ) |
break; |
} |
CYG_ASSERT( 0 < i, "EEPROM reload timout!" ); |
// Now extract the MAC address that is in the chip, and tell the |
// system about it. |
for (i = 0; i < sizeof(cpd->enaddr); i += 2) { |
unsigned short z = get_reg(sc, LAN91CXX_IA01+i/2 ); |
cpd->enaddr[i] = (unsigned char)(0xff & z); |
cpd->enaddr[i+1] = (unsigned char)(0xff & (z >> 8)); |
} |
#if DEBUG & 9 |
diag_printf("LAN91CXX - eeprom new ESA: %02x:%02x:%02x:%02x:%02x:%02x\n", |
cpd->enaddr[0], |
cpd->enaddr[1], |
cpd->enaddr[2], |
cpd->enaddr[3], |
cpd->enaddr[4], |
cpd->enaddr[5] ); |
#endif // DEBUG |
for (i = 0; i < sizeof(cpd->enaddr); i++ ) { |
CYG_ASSERT( esa[i] == cpd->enaddr[i], "ESA not written correctly" ); |
if ( esa[i] != cpd->enaddr[i] ) |
return 1; // the operation failed. |
} |
#else // not CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM |
// Whatever, we can write the MAC address into the interface info, |
// and the chip registers no problem. |
for ( i = 0; i < sizeof(cpd->enaddr); i++ ) |
cpd->enaddr[i] = esa[i]; |
for (i = 0; i < sizeof(cpd->enaddr); i += 2) { |
reg = cpd->enaddr[i] | (cpd->enaddr[i+1] << 8); |
put_reg(sc, LAN91CXX_IA01+i/2, reg ); |
} |
#endif // !CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM |
return 0; |
|
#ifdef ETH_DRV_GET_MAC_ADDRESS |
case ETH_DRV_GET_MAC_ADDRESS: |
// Extract the MAC address that is in the chip, and tell the |
// system about it. |
for (i = 0; i < sizeof(cpd->enaddr); i += 2) { |
unsigned short z = get_reg(sc, LAN91CXX_IA01+i/2 ); |
esa[i] = (unsigned char)(0xff & z); |
esa[i+1] = (unsigned char)(0xff & (z >> 8)); |
} |
return 0; |
#endif |
|
#ifdef ETH_DRV_GET_IF_STATS_UD |
case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE |
#endif |
// drop through |
#ifdef ETH_DRV_GET_IF_STATS |
case ETH_DRV_GET_IF_STATS: |
#endif |
#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD) |
{ |
struct ether_drv_stats *p = (struct ether_drv_stats *)data; |
// Chipset entry is no longer supported; RFC1573. |
for ( i = 0; i < SNMP_CHIPSET_LEN; i++ ) |
p->snmp_chipset[i] = 0; |
|
// This perhaps should be a config opt, so you can make up your own |
// description, or supply it from the instantiation. |
strcpy( p->description, "SMSC LAN91Cxx" ); |
// CYG_ASSERT( 48 > strlen(p->description), "Description too long" ); |
|
reg = get_reg(sc, LAN91CXX_EPH_STATUS); |
if ((reg & LAN91CXX_STATUS_LINK_OK) == 0) { |
p->operational = 2; // LINK DOWN |
p->duplex = 1; // UNKNOWN |
p->speed = 0; |
} |
else { |
p->operational = 3; // LINK UP |
p->duplex = 2; // 2 = SIMPLEX, 3 = DUPLEX |
p->speed = 10 * 1000000; // it's only a 10Mbit device |
} |
|
#ifdef KEEP_STATISTICS |
{ |
struct smsc_lan91cxx_stats *ps = &(cpd->stats); |
|
// Admit to it... |
p->supports_dot3 = true; |
|
p->tx_good = ps->tx_good ; |
p->tx_max_collisions = ps->tx_max_collisions ; |
p->tx_late_collisions = ps->tx_late_collisions ; |
p->tx_underrun = ps->tx_underrun ; |
p->tx_carrier_loss = ps->tx_carrier_loss ; |
p->tx_deferred = ps->tx_deferred ; |
p->tx_sqetesterrors = ps->tx_sqetesterrors ; |
p->tx_single_collisions = ps->tx_single_collisions; |
p->tx_mult_collisions = ps->tx_mult_collisions ; |
p->tx_total_collisions = ps->tx_total_collisions ; |
p->rx_good = ps->rx_good ; |
p->rx_crc_errors = ps->rx_crc_errors ; |
p->rx_align_errors = ps->rx_align_errors ; |
p->rx_resource_errors = ps->rx_resource_errors ; |
p->rx_overrun_errors = ps->rx_overrun_errors ; |
p->rx_collisions = ps->rx_collisions ; |
p->rx_short_frames = ps->rx_short_frames ; |
p->rx_too_long_frames = ps->rx_too_long_frames ; |
p->rx_symbol_errors = ps->rx_symbol_errors ; |
|
p->interrupts = ps->interrupts ; |
p->rx_count = ps->rx_count ; |
p->rx_deliver = ps->rx_deliver ; |
p->rx_resource = ps->rx_resource ; |
p->rx_restart = ps->rx_restart ; |
p->tx_count = ps->tx_count ; |
p->tx_complete = ps->tx_complete ; |
p->tx_dropped = ps->tx_dropped ; |
} |
#endif // KEEP_STATISTICS |
|
p->tx_queue_len = 1; |
|
return 0; // OK |
} |
#endif |
default: |
} |
return 1; |
} |
|
// |
// 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 |
lan91cxx_can_send(struct eth_drv_sc *sc) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
int tcr; |
|
DEBUG_FUNCTION(); |
|
#ifndef LAN91CXX_IS_LAN91C111 |
// LINK_OK on 91C111 is just a general purpose input and may not |
// have anything to do with the link. |
if ((get_reg(sc, LAN91CXX_EPH_STATUS) & LAN91CXX_STATUS_LINK_OK) == 0) { |
diag_printf("no link\n"); |
return false; // Link not connected |
} |
#endif |
|
CYG_ASSERT( cpd->within_send < 10, "can_send: Excess send recursions" ); |
cpd->within_send++; |
|
tcr = get_reg(sc, LAN91CXX_TCR); |
if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) { |
#if DEBUG & 1 |
diag_printf("%s: ENGINE RESTART: tcr %x\n", __FUNCTION__, tcr ); |
#endif |
// Complete any outstanding activity: |
if ( cpd->txbusy ) { |
cpd->txbusy = 0; |
#if DEBUG & 9 |
diag_printf("LAN91CXX - can_send, cleaning up pending TX\n" ); |
#endif |
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0); |
} |
tcr |= LAN91CXX_TCR_TXENA; |
put_reg(sc, LAN91CXX_TCR, tcr); |
} |
|
// This helps unstick deadly embraces. |
lan91cxx_poll( sc ); // Deal with any outstanding rx state |
cpd->within_send--; |
|
return (cpd->txbusy == 0) && (0 == cpd->within_send); |
} |
|
// |
// This routine is called to send data to the hardware. |
static void |
lan91cxx_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, |
int total_len, unsigned long key) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
int i, len, plen, tcr; |
|
unsigned short *sdata = NULL; |
unsigned short ints, control; |
cyg_uint16 packet, status; |
|
DEBUG_FUNCTION(); |
|
INCR_STAT( tx_count ); |
|
// Worry about the TX engine stopping. |
tcr = get_reg(sc, LAN91CXX_TCR); |
if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) { |
#if DEBUG & 1 |
diag_printf("%s: ENGINE RESTART: tcr %x\n", __FUNCTION__, tcr ); |
#endif |
tcr |= LAN91CXX_TCR_TXENA; |
put_reg(sc, LAN91CXX_TCR, tcr); |
} |
|
// This helps unstick deadly embraces. |
CYG_ASSERT( cpd->within_send < 10, "send: Excess send recursions" ); |
cpd->within_send++; |
lan91cxx_poll( sc ); // Deal with any outstanding rx state |
cpd->within_send--; |
|
cpd->txbusy = 1; |
cpd->txkey = key; |
|
// Find packet length |
plen = 0; |
for (i = 0; i < sg_len; i++) |
plen += sg_list[i].len; |
|
CYG_ASSERT( plen == total_len, "sg data length mismatch" ); |
|
// Alloc new TX packet |
do { |
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_alloc_for_tx |
#ifndef LAN91CXX_IS_LAN91C111 |
| ((plen >> 8) & 0x07) |
#endif |
); |
|
i = 1024 * 1024; |
do { |
status = get_reg(sc, LAN91CXX_INTERRUPT); |
} while (0 == (status & LAN91CXX_INTERRUPT_ALLOC_INT) && (--i > 0) ); |
if ( i ) |
packet = get_reg(sc, LAN91CXX_PNR); |
else |
packet = 0xffff; |
#if DEBUG & 1 |
diag_printf("%s: allocated packet %04x\n", __FUNCTION__, packet); |
#endif |
packet = packet >> 8; |
if (packet & 0x80) { |
// Hm.. Isn't this a dead end? |
#if DEBUG & 1 |
diag_printf("%s: Allocation failed! Retrying...\n", __FUNCTION__ ); |
#endif |
// Not if we can make progress with what's filling memory. |
lan91cxx_poll( sc ); // Deal with any outstanding state |
continue; |
} |
} while (0); |
|
#if DEBUG & 4 |
diag_printf("#####Tx packet allocated %x (previous %x)\n", |
packet, cpd->txpacket); |
#endif |
cpd->txpacket = packet; |
|
put_reg(sc, LAN91CXX_PNR, packet); |
// Note: Check FIFO state here before continuing? |
put_reg(sc, LAN91CXX_POINTER, LAN91CXX_POINTER_AUTO_INCR | 0x0000); |
// Pointer is now set, and the proper bank is selected for |
// data writes. |
|
// Prepare header: |
put_data(sc, 0); // reserve space for status word |
// packet length (includes status, byte-count and control shorts) |
put_data(sc, 0x7FE & (plen + 6) ); // Always even, always < 15xx(dec) |
|
// Put data into buffer |
for (i = 0; i < sg_len; i++) { |
sdata = (unsigned short *)sg_list[i].buf; |
len = sg_list[i].len; |
|
CYG_ASSERT(0 == (len & 1) || (i == (sg_len-1)), "odd length"); |
CYG_ASSERT( sdata, "No sg data pointer here" ); |
while(len >= sizeof(*sdata)) { |
put_data(sc, *sdata++); |
len -= sizeof(*sdata); |
} |
} |
CYG_ASSERT( sdata, "No sg data pointer outside" ); |
|
// Lay down the control short unconditionally at the end. |
// (or it might use random memory contents) |
control = 0; |
if ( 1 & plen ) { |
// Need to set ODD flag and insert the data |
unsigned char onebyte = *(unsigned char*)sdata; |
control = onebyte; |
control |= LAN91CXX_CONTROLBYTE_ODD; |
} |
control |= LAN91CXX_CONTROLBYTE_CRC; // Just in case... |
put_data(sc, control); |
|
// Enqueue the packet |
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_enq_packet); |
|
// Ack TX empty int and unmask it. |
ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00; |
put_reg(sc, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_SET_ACK); |
put_reg(sc, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_SET_M); |
|
#if DEBUG & 1 |
ints = get_reg(sc, LAN91CXX_INTERRUPT); |
diag_printf("%s:END: ints at TX: %04x\n", __FUNCTION__, ints); |
#endif |
} |
|
static void |
lan91cxx_TxEvent(struct eth_drv_sc *sc, int stat) |
{ |
unsigned short packet, ints, tcr; |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
int success = 1; |
|
DEBUG_FUNCTION(); |
|
INCR_STAT( tx_complete ); |
|
// Ack and mask TX interrupt set |
ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00; |
ints |= LAN91CXX_INTERRUPT_TX_SET_ACK; |
ints &= ~LAN91CXX_INTERRUPT_TX_SET_M; |
put_reg(sc, LAN91CXX_INTERRUPT, ints); |
|
// Get number of completed packet and read the status word |
packet = get_reg(sc, LAN91CXX_FIFO_PORTS); |
#if DEBUG & 1 |
diag_printf("%s:START: fifo %04x ints %04x\n", __FUNCTION__, packet, ints); |
#endif |
|
#ifdef KEEP_STATISTICS |
{ |
unsigned short reg; |
|
reg = get_reg( sc, LAN91CXX_EPH_STATUS ); |
|
// Covering each bit in turn... |
if ( reg & LAN91CXX_STATUS_TX_UNRN ) INCR_STAT( tx_underrun ); |
//if ( reg & LAN91CXX_STATUS_LINK_OK ) INCR_STAT( ); |
//if ( reg & LAN91CXX_STATUS_CTR_ROL ) INCR_STAT( ); |
//if ( reg & LAN91CXX_STATUS_EXC_DEF ) INCR_STAT( ); |
if ( reg & LAN91CXX_STATUS_LOST_CARR ) INCR_STAT( tx_carrier_loss ); |
if ( reg & LAN91CXX_STATUS_LATCOL ) INCR_STAT( tx_late_collisions ); |
//if ( reg & LAN91CXX_STATUS_WAKEUP ) INCR_STAT( ); |
if ( reg & LAN91CXX_STATUS_TX_DEFR ) INCR_STAT( tx_deferred ); |
//if ( reg & LAN91CXX_STATUS_LTX_BRD ) INCR_STAT( ); |
if ( reg & LAN91CXX_STATUS_SQET ) INCR_STAT( tx_sqetesterrors ); |
if ( reg & LAN91CXX_STATUS_16COL ) INCR_STAT( tx_max_collisions ); |
//if ( reg & LAN91CXX_STATUS_LTX_MULT) INCR_STAT( ); |
if ( reg & LAN91CXX_STATUS_MUL_COL ) INCR_STAT( tx_mult_collisions ); |
if ( reg & LAN91CXX_STATUS_SNGL_COL ) INCR_STAT( tx_single_collisions ); |
if ( reg & LAN91CXX_STATUS_TX_SUC ) INCR_STAT( tx_good ); |
|
cpd->stats.tx_total_collisions = |
cpd->stats.tx_late_collisions + |
cpd->stats.tx_max_collisions + |
cpd->stats.tx_mult_collisions + |
cpd->stats.tx_single_collisions; |
|
// We do not need to look in the Counter Register (LAN91CXX_COUNTER) |
// because it just mimics the info we already have above. |
} |
#endif // KEEP_STATISTICS |
// We do not really care about Tx failure. Ethernet is not a reliable |
// medium. But we do care about the TX engine stopping. |
tcr = get_reg(sc, LAN91CXX_TCR); |
if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) { |
#if DEBUG & 1 |
diag_printf("%s: ENGINE RESTART: tcr %x ints %04x\n", __FUNCTION__, tcr, ints); |
#endif |
tcr |= LAN91CXX_TCR_TXENA; |
put_reg(sc, LAN91CXX_TCR, tcr); |
success = 0; // And treat this as an error... |
} |
|
packet &= 0xff; |
|
// It certainly appears that occasionally the tx fifo tells lies; we |
// get the wrong packet number. Freeing the one we allocated seems to |
// give correct operation. |
#ifdef CYGPKG_INFRA_DEBUG |
// Then we log, OOI, the number of times we get a bad packet number |
// from the tx done fifo. |
if (cpd->txpacket != packet ) |
lan91cxx_txfifo_bad++; |
else |
lan91cxx_txfifo_good++; |
#endif |
#if DEBUG & 4 |
diag_printf("#####Tx packet freed %x (expected %x)\n", packet, cpd->txpacket ); |
#endif |
// and then free the packet |
put_reg(sc, LAN91CXX_PNR, cpd->txpacket); |
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_rel_packet); |
|
// Ack the TX int which is supposed to clear the packet from the TX |
// completion queue. |
ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00; |
ints |= LAN91CXX_INTERRUPT_TX_FIFO_ACK; |
put_reg(sc, LAN91CXX_INTERRUPT, ints); |
|
#if DEBUG & 1 |
// Hm... The free doesn't seem to have the desired effect?!? |
ints = get_reg(sc, LAN91CXX_INTERRUPT); |
packet = get_reg(sc, LAN91CXX_FIFO_PORTS); |
diag_printf("%s:END: fifo %04x ints %04x\n", __FUNCTION__, packet, ints); |
#endif |
|
if ( cpd->txbusy ) { |
cpd->txbusy = 0; |
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, success); |
} |
} |
|
|
// |
// This function is called when a packet has been received. Its 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 |
// 'lan91cxx_recv' will be called to actually fetch it from the hardware. |
// |
static void |
lan91cxx_RxEvent(struct eth_drv_sc *sc) |
{ |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
unsigned short stat, len; |
#ifdef LAN91CXX_32BIT_RX |
cyg_uint32 val; |
#endif |
|
DEBUG_FUNCTION(); |
|
stat = get_reg(sc, LAN91CXX_FIFO_PORTS); |
#if DEBUG & 1 |
diag_printf("RxEvent - FIFOs: 0x%04x\n", stat); |
#endif |
if ( 0x8000 & stat ) { |
// Then the Rx FIFO is empty |
#if DEBUG & 4 |
diag_printf("#####RxEvent with empty fifo\n"); |
#endif |
return; |
} |
|
INCR_STAT( rx_count ); |
|
#if DEBUG & 4 |
diag_printf("#####Rx packet allocated %x (previous %x)\n", |
0xff & (stat >> 8), cpd->rxpacket ); |
#endif |
// There is an Rx Packet ready |
cpd->rxpacket = 0xff & (stat >> 8); |
|
// Read status and (word) length |
put_reg(sc, LAN91CXX_POINTER, (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ | |
LAN91CXX_POINTER_AUTO_INCR | 0x0000)); |
#ifdef LAN91CXX_32BIT_RX |
val = get_data(sc); |
stat = val & 0xffff; |
len = ((val >> 16) & 0xffff) - 6; // minus header/footer words |
#else |
stat = get_data(sc); |
len = get_data(sc) - 6; // minus header/footer words |
#endif |
|
#ifdef KEEP_STATISTICS |
if ( stat & LAN91CXX_RX_STATUS_ALIGNERR ) INCR_STAT( rx_align_errors ); |
//if ( stat & LAN91CXX_RX_STATUS_BCAST ) INCR_STAT( ); |
if ( stat & LAN91CXX_RX_STATUS_BADCRC ) INCR_STAT( rx_crc_errors ); |
if ( stat & LAN91CXX_RX_STATUS_TOOLONG ) INCR_STAT( rx_too_long_frames ); |
if ( stat & LAN91CXX_RX_STATUS_TOOSHORT ) INCR_STAT( rx_short_frames ); |
//if ( stat & LAN91CXX_RX_STATUS_MCAST ) INCR_STAT( ); |
#endif // KEEP_STATISTICS |
|
if ((stat & LAN91CXX_RX_STATUS_BAD) == 0) { |
INCR_STAT( rx_good ); |
// Then it's OK |
|
if (stat & LAN91CXX_RX_STATUS_ODDFRM) |
len++; |
|
#if DEBUG & 1 |
diag_printf("RxEvent good rx - stat: 0x%04x, len: 0x%04x\n", stat, len); |
#endif |
// Check for bogusly short packets; can happen in promisc mode: |
// Asserted against and checked by upper layer driver. |
#ifdef CYGPKG_NET |
if ( len > sizeof( struct ether_header ) ) |
// then it is acceptable; offer the data to the network stack |
#endif |
(sc->funs->eth_drv->recv)(sc, len); |
|
return; |
} |
|
// Not OK for one reason or another... |
#if DEBUG & 1 |
diag_printf("RxEvent - bad rx: stat: 0x%04x, len: 0x%04x\n", stat, len); |
#endif |
|
// Free packet |
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame); |
} |
|
// |
// This function is called as a result of the "eth_drv_recv()" call above. |
// Its 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 |
lan91cxx_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len) |
{ |
#if (4 & DEBUG) || defined(CYGPKG_INFRA_DEBUG) || defined(KEEP_STATISTICS) |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
#endif |
int i, mlen=0, plen; |
rxd_t *data=NULL, val; |
unsigned char *cp, cval; |
|
DEBUG_FUNCTION(); |
|
INCR_STAT( rx_deliver ); |
|
put_reg(sc, LAN91CXX_POINTER, (LAN91CXX_POINTER_RCV | LAN91CXX_POINTER_READ | |
LAN91CXX_POINTER_AUTO_INCR)); |
val = get_data(sc); |
|
// packet length (minus header/footer) |
#ifdef LAN91CXX_32BIT_RX |
plen = (val >> 16) - 6; |
#else |
plen = get_data(sc) - 6; |
#endif |
if (val & LAN91CXX_RX_STATUS_ODDFRM) |
plen++; |
|
for (i = 0; i < sg_len; i++) { |
data = (rxd_t *)sg_list[i].buf; |
mlen = sg_list[i].len; |
|
CYG_ASSERT(0 == (mlen & (sizeof(*data) - 1)) || (i == (sg_len-1)), "odd length"); |
|
#if DEBUG & 1 |
diag_printf("%s : mlen %x, plen %x\n", __FUNCTION__, mlen, plen); |
#endif |
if (data) { |
while (mlen >= sizeof(*data)) { |
*data++ = get_data(sc); |
mlen -= sizeof(*data); |
plen -= sizeof(*data); |
} |
} |
else { // must actively discard ie. read it from the chip anyway. |
while (mlen >= sizeof(*data)) { |
(void)get_data(sc); |
mlen -= sizeof(*data); |
plen -= sizeof(*data); |
} |
} |
} |
val = get_data(sc); // Read control word (and potential data) unconditionally |
#ifdef LAN91CXX_32BIT_RX |
if (plen & 2) { |
if (data) |
*(cyg_uint16 *)data = val & 0xffff; |
cp = (unsigned char *)data + 2; |
val >>= 16; |
mlen -= 2; |
} else |
#endif |
cp = (unsigned char *)data; |
|
CYG_ASSERT(val & LAN91CXX_CONTROLBYTE_RX, |
"Controlbyte is not for Rx"); |
CYG_ASSERT( (1 == mlen) == (0 != (val & LAN91CXX_CONTROLBYTE_ODD)), |
"Controlbyte does not match"); |
if (data && (1 == mlen) && (val & LAN91CXX_CONTROLBYTE_ODD)) { |
cval = val & 0x00ff; // last byte contains data |
*cp = cval; |
} |
|
val = get_reg(sc, LAN91CXX_FIFO_PORTS); |
#if DEBUG & 4 |
if ( 0x8000 & val ) // Then the Rx FIFO is empty |
diag_printf("#####Rx packet NOT freed, stat is %x (expected %x)\n", |
val, cpd->rxpacket); |
else |
diag_printf("#####Rx packet freed %x (expected %x)\n", |
0xff & (val >> 8), cpd->rxpacket ); |
#endif |
CYG_ASSERT( (0xff & (val >> 8)) == cpd->rxpacket, "Unexpected rx packet" ); |
|
// Free packet |
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_remrel_rx_frame); |
} |
|
static void |
lan91cxx_poll(struct eth_drv_sc *sc) |
{ |
unsigned short event; |
struct lan91cxx_priv_data *cpd = |
(struct lan91cxx_priv_data *)sc->driver_private; |
|
DEBUG_FUNCTION(); |
while (1) { |
cyg_drv_interrupt_acknowledge(cpd->interrupt); |
// Get the (unmasked) requests |
event = get_reg(sc, LAN91CXX_INTERRUPT); |
event = event & (event >> 8) & 0xff; |
if (0 == event) |
break; |
#if 0 |
if (event & LAN91CXX_INTERRUPT_ERCV_INT) { |
// Early receive interrupt |
} |
else if (event & LAN91CXX_INTERRUPT_EPH_INT) { |
// ethernet protocol handler failures |
} |
else if (event & LAN91CXX_INTERRUPT_RX_OVRN_INT) { |
// receive overrun |
} |
else if (event & LAN91CXX_INTERRUPT_ALLOC_INT) { |
// allocation interrupt |
} |
else |
#endif |
if (event & LAN91CXX_INTERRUPT_TX_SET) { |
lan91cxx_TxEvent(sc, event); |
} |
if (event & LAN91CXX_INTERRUPT_RCV_INT) { |
lan91cxx_RxEvent(sc); |
} |
if (event & ~(LAN91CXX_INTERRUPT_TX_SET | LAN91CXX_INTERRUPT_RCV_INT)) |
diag_printf("%s: Unknown interrupt: 0x%04x\n", |
__FUNCTION__, event); |
} |
} |
|
#ifdef LAN91CXX_IS_LAN91C111 |
|
static cyg_uint16 |
lan91cxx_read_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr, cyg_uint8 phyreg) |
{ |
int i, mask, input_idx, clk_idx = 0; |
cyg_uint16 mii_reg, value; |
cyg_uint8 bits[64]; |
|
// 32 consecutive ones on MDO to establish sync |
for (i = 0; i < 32; ++i) |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
|
// Start code <01> |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE; |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
|
// Read command <10> |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE; |
|
// Output the PHY address, msb first |
for (mask = 0x10; mask; mask >>= 1) { |
if (phyaddr & mask) |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
else |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE; |
} |
|
// Output the phy register number, msb first |
for (mask = 0x10; mask; mask >>= 1) { |
if (phyreg & mask) |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
else |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE; |
} |
|
// Tristate and turnaround (1 bit times) |
bits[clk_idx++] = 0; |
|
// Input starts at this bit time |
input_idx = clk_idx; |
|
// Will input 16 bits |
for (i = 0; i < 16; ++i) |
bits[clk_idx++] = 0; |
|
// Final clock bit |
bits[clk_idx++] = 0; |
|
// Get the current MII register value |
mii_reg = get_reg(sc, LAN91CXX_MGMT); |
|
// Turn off all MII Interface bits |
mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK | |
LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO); |
|
// Clock all 64 cycles |
for (i = 0; i < sizeof(bits); ++i) { |
// Clock Low - output data |
put_reg(sc, LAN91CXX_MGMT, mii_reg | bits[i]); |
HAL_DELAY_US(50); |
|
// Clock Hi - input data |
put_reg(sc, LAN91CXX_MGMT, mii_reg | bits[i] | LAN91CXX_MGMT_MCLK); |
HAL_DELAY_US(50); |
|
bits[i] |= get_reg(sc, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI; |
} |
|
// Return to idle state |
put_reg(sc, LAN91CXX_MGMT, mii_reg); |
HAL_DELAY_US(50); |
|
// Recover input data |
for (value = 0, i = 0; i < 16; ++i) { |
value <<= 1; |
if (bits[input_idx++] & LAN91CXX_MGMT_MDI) |
value |= 1; |
} |
return value; |
} |
|
static void |
lan91cxx_write_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr, |
cyg_uint8 phyreg, cyg_uint16 value) |
{ |
int i, mask, clk_idx = 0; |
cyg_uint16 mii_reg; |
cyg_uint8 bits[65]; |
|
// 32 consecutive ones on MDO to establish sync |
for (i = 0; i < 32; ++i) |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
|
// Start code <01> |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE; |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
|
// Write command <01> |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE; |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
|
// Output the PHY address, msb first |
for (mask = 0x10; mask; mask >>= 1) { |
if (phyaddr & mask) |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
else |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE; |
} |
|
// Output the phy register number, msb first |
for (mask = 0x10; mask; mask >>= 1) { |
if (phyreg & mask) |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
else |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE; |
} |
|
// Tristate and turnaround (2 bit times) |
bits[clk_idx++] = 0; |
bits[clk_idx++] = 0; |
|
// Write out 16 bits of data, msb first |
for (mask = 0x8000; mask; mask >>= 1) { |
if (value & mask) |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MDO; |
else |
bits[clk_idx++] = LAN91CXX_MGMT_MDOE; |
} |
|
// Final clock bit (tristate) |
bits[clk_idx++] = 0; |
|
// Get the current MII register value |
mii_reg = get_reg(sc, LAN91CXX_MGMT); |
|
// Turn off all MII Interface bits |
mii_reg &= ~(LAN91CXX_MGMT_MDOE | LAN91CXX_MGMT_MCLK | |
LAN91CXX_MGMT_MDI | LAN91CXX_MGMT_MDO); |
|
// Clock all cycles |
for (i = 0; i < sizeof(bits); ++i) { |
// Clock Low - output data |
put_reg(sc, LAN91CXX_MGMT, mii_reg | bits[i]); |
HAL_DELAY_US(50); |
|
// Clock Hi - input data |
put_reg(sc, LAN91CXX_MGMT, mii_reg | bits[i] | LAN91CXX_MGMT_MCLK); |
HAL_DELAY_US(50); |
|
// bits[i] |= get_reg(sc, LAN91CXX_MGMT) & LAN91CXX_MGMT_MDI; |
} |
|
// Return to idle state |
put_reg(sc, LAN91CXX_MGMT, mii_reg); |
HAL_DELAY_US(50); |
} |
#endif // LAN91CXX_IS_LAN91C111 |
|
// EOF if_lan91cxx.c |