OpenCores
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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.