freebsd-nq/sys/dev/hea/eni.c
Andrew R. Reiter b6037a7953 - Turn the hea and hfa HARP storage pools into UMA zones and insert
the necesary uma_zcreate() and uma_zdestroy calls into module loading
  handler and the device attach handling.
- Change the related HARP netatm code to use UMA zone functions when
  dealing with the zones that were formerly the ATM interface (hea, hfa)
  storage pools.
- Have atm_physif_freenifs() now get passed an uma_zone_t so that we can
  properly free the allocated NIF's back to their zone.

This should be the last commit to remove any code that makes use of the
netatm storage pool api.  I will be removing the api code within the near
future.

Reviewed by:	mdodd
2002-06-14 16:59:38 +00:00

696 lines
15 KiB
C

/*
*
* ===================================
* HARP | Host ATM Research Platform
* ===================================
*
*
* This Host ATM Research Platform ("HARP") file (the "Software") is
* made available by Network Computing Services, Inc. ("NetworkCS")
* "AS IS". NetworkCS does not provide maintenance, improvements or
* support of any kind.
*
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
* In no event shall NetworkCS be responsible for any damages, including
* but not limited to consequential damages, arising from or relating to
* any use of the Software or related support.
*
* Copyright 1994-1998 Network Computing Services, Inc.
*
* Copies of this Software may be made, however, the above copyright
* notice must be reproduced on all copies.
*
* @(#) $FreeBSD$
*
*/
/*
* Efficient ENI adapter support
* -----------------------------
*
* Module supports PCI interface to ENI adapter
*
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syslog.h>
#include <sys/eventhandler.h>
#include <net/if.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/hea/eni_stats.h>
#include <dev/hea/eni.h>
#include <dev/hea/eni_var.h>
#ifndef lint
__RCSID("@(#) $FreeBSD$");
#endif
/*
* Typedef local functions
*/
#if 0
static const char *eni_pci_probe(pcici_t, pcidi_t);
static void eni_pci_attach(pcici_t, int);
#endif
static int eni_get_ack(Eni_unit *);
static int eni_get_sebyte(Eni_unit *);
void eni_read_seeprom(Eni_unit *);
#if 0
#if BSD < 199506
static int eni_pci_shutdown(struct kern_devconf *, int);
#else
static void eni_pci_shutdown(void *, int);
#endif
static void eni_pci_reset(Eni_unit *);
#endif
#if 0
/*
* Used by kernel to return number of claimed devices
*/
static u_long eni_nunits;
static struct pci_device eni_pci_device = {
ENI_DEV_NAME,
eni_pci_probe,
eni_pci_attach,
&eni_nunits,
#if BSD < 199506
eni_pci_shutdown
#else
NULL
#endif
};
COMPAT_PCI_DRIVER (eni_pci, eni_pci_device);
/*
* Called by kernel with PCI device_id which was read from the PCI
* register set. If the identified vendor is Efficient, see if we
* recognize the particular device. If so, return an identifying string,
* if not, return null.
*
* Arguments:
* config_id PCI config token
* device_id contents of PCI device ID register
*
* Returns:
* string Identifying string if we will handle this device
* NULL unrecognized vendor/device
*
*/
static const char *
eni_pci_probe ( pcici_t config_id, pcidi_t device_id )
{
if ( (device_id & 0xFFFF) == EFF_VENDOR_ID ) {
switch ( (device_id >> 16) ) {
case EFF_DEV_ID:
return ( "Efficient ENI ATM Adapter" );
/* NOTREACHED */
break;
}
}
return ( NULL );
}
#endif
/*
* The ENI-155p adapter uses an ATMEL AT24C01 serial EEPROM to store
* configuration information. The SEEPROM is accessed via two wires,
* CLOCK and DATA, which are accessible via the PCI configuration
* registers. The following macros manipulate the lines to access the
* SEEPROM. See http://www.atmel.com/atmel/products/prod162.htm for
* a description of the AT24C01 part. Value to be read/written is
* part of the per unit structure.
*/
/*
* Write bits to SEEPROM
*/
#define WRITE_SEEPROM() ( \
{ \
(void) pci_write_config(eup->eu_pcitag, SEEPROM, \
eup->eu_sevar, 4); \
DELAY(SEPROM_DELAY); \
} \
)
/*
* Stobe first the DATA, then the CLK lines high
*/
#define STROBE_HIGH() ( \
{ \
eup->eu_sevar |= SEPROM_DATA; WRITE_SEEPROM(); \
eup->eu_sevar |= SEPROM_CLK; WRITE_SEEPROM(); \
} \
)
/*
* Strobe first the CLK, then the DATA lines high
*/
#define INV_STROBE_HIGH() ( \
{ \
eup->eu_sevar |= SEPROM_CLK; WRITE_SEEPROM(); \
eup->eu_sevar |= SEPROM_DATA; WRITE_SEEPROM(); \
} \
)
/*
* Strobe first the CLK, then the DATA lines low - companion to
* STROBE_HIGH()
*/
#define STROBE_LOW() ( \
{ \
eup->eu_sevar &= ~SEPROM_CLK; WRITE_SEEPROM(); \
eup->eu_sevar &= ~SEPROM_DATA; WRITE_SEEPROM(); \
} \
)
/*
* Strobe first the DATA, then the CLK lines low - companion to
* INV_STROBE_HIGH()
*/
#define INV_STROBE_LOW() ( \
{ \
eup->eu_sevar &= ~SEPROM_DATA; WRITE_SEEPROM(); \
eup->eu_sevar &= ~SEPROM_CLK; WRITE_SEEPROM(); \
} \
)
/*
* Strobe the CLK line high, then low
*/
#define STROBE_CLK() ( \
{ \
eup->eu_sevar |= SEPROM_CLK; WRITE_SEEPROM(); \
eup->eu_sevar &= ~SEPROM_CLK; WRITE_SEEPROM(); \
} \
)
/*
* Look for a positive ACK from the SEEPROM. Cycle begins by asserting
* the DATA line, then the CLK line. The DATA line is then read to
* retrieve the ACK status, and then the cycle is finished by deasserting
* the CLK line, and asserting the DATA line.
*
* Arguments:
* eup pointer to per unit structure
*
* Returns:
* 0/1 value of ACK
*
*/
static int
eni_get_ack (eup)
Eni_unit *eup;
{
int ack;
STROBE_HIGH();
/*
* Read DATA line from SEPROM
*/
eup->eu_sevar = pci_read_config(eup->eu_pcitag, SEEPROM, 4);
DELAY ( SEPROM_DELAY );
ack = eup->eu_sevar & SEPROM_DATA;
eup->eu_sevar &= ~SEPROM_CLK;
WRITE_SEEPROM();
eup->eu_sevar |= SEPROM_DATA;
WRITE_SEEPROM();
return ( ack );
}
/*
* Read a byte from the SEEPROM. Data is read as 8 bits. There are two types
* of read operations. The first is a single byte read, the second is
* multiple sequential bytes read. Both cycles begin with a 'START' operation,
* followed by a memory address word. Following the memory address, the
* SEEPROM will send a data byte, followed by an ACK. If the host responds
* with a 'STOP' operation, then a single byte cycle is performed. If the
* host responds with an 'ACK', then the memory address is incremented, and
* the next sequential memory byte is serialized.
*
* Arguments:
* eup pointer to per unit structure
*
* Returns:
* val value of byte read from SEEPROM
*
*/
static int
eni_get_sebyte(eup)
Eni_unit *eup;
{
int i;
int data;
int rval;
/* Initial value */
rval = 0;
/* Read 8 bits */
for ( i = 0; i < 8; i++ ) {
/* Shift bits to left so the next bit goes to position 0 */
rval <<= 1;
/* Indicate we're ready to read bit */
STROBE_HIGH();
/*
* Read DATA line from SEPROM
*/
data = pci_read_config(eup->eu_pcitag, SEEPROM, 4);
DELAY ( SEPROM_DELAY );
/* (Possibly) mask bit into accumulating value */
if ( data & SEPROM_DATA )
rval |= 1; /* If DATA bit '1' */
/* Indicate we're done reading this bit */
STROBE_LOW();
}
/* Return acquired byte */
return ( rval );
}
/*
* The AT24C01 is a 1024 bit part organized as 128 words by 8 bits.
* We will read the entire contents into the per unit structure. Later,
* we'll retrieve the MAC address and serial number from the data read.
*
* Arguments:
* eup pointer to per unit structure
*
* Returns:
* none
*
*/
void
eni_read_seeprom ( eup )
Eni_unit *eup;
{
int addr;
int i, j;
/*
* Set initial state
*/
eup->eu_sevar = SEPROM_DATA | SEPROM_CLK;
WRITE_SEEPROM ();
/* Loop for all bytes */
for ( i = 0; i < SEPROM_SIZE ; i++ ) {
/* Send START operation */
STROBE_HIGH();
INV_STROBE_LOW();
/*
* Send address. Addresses are sent as 7 bits plus
* last bit high.
*/
addr = ((i) << 1) + 1;
/*
* Start with high order bit first working toward low
* order bit.
*/
for ( j = 7; j >= 0; j-- ) {
/* Set current bit value */
eup->eu_sevar = ( addr >> j ) & 1 ?
eup->eu_sevar | SEPROM_DATA :
eup->eu_sevar & ~SEPROM_DATA;
WRITE_SEEPROM ();
/* Indicate we've sent it */
STROBE_CLK();
}
/*
* We expect a zero ACK after sending the address
*/
if ( !eni_get_ack ( eup ) ) {
/* Address okay - read data byte */
eup->eu_seeprom[i] = eni_get_sebyte ( eup );
/* Grab but ignore the ACK op */
(void) eni_get_ack ( eup );
} else {
/* Address ACK was bad - can't retrieve data byte */
eup->eu_seeprom[i] = 0xff;
}
}
return;
}
#if 0
/*
* The kernel has found a device which we are willing to support.
* We are now being called to do any necessary work to make the
* device initially usable. In our case, this means allocating
* structure memory, configuring registers, mapping device
* memory, setting pointers, registering with the core services,
* and doing the initial PDU processing configuration.
*
* Arguments:
* config_id PCI device token
* unit instance of the unit
*
* Returns:
* none
*
*/
static void
eni_pci_attach ( pcici_t config_id, int unit )
{
vm_offset_t va;
vm_offset_t pa;
Eni_unit *eup;
long val;
/*
* Just checking...
*/
if ( unit >= ENI_MAX_UNITS ) {
log ( LOG_ERR, "%s%d: too many devices\n",
ENI_DEV_NAME, unit );
return;
}
/*
* Make sure this isn't a duplicate unit
*/
if ( eni_units[unit] != NULL )
return;
/*
* Allocate a new unit structure
*/
eup = (Eni_unit *) atm_dev_alloc ( sizeof(Eni_unit), sizeof(int), 0 );
if ( eup == NULL )
return;
/*
* Start initializing it
*/
eup->eu_unit = unit;
eup->eu_mtu = ENI_IFF_MTU;
eup->eu_pcitag = config_id;
eup->eu_ioctl = eni_atm_ioctl;
eup->eu_instvcc = eni_instvcc;
eup->eu_openvcc = eni_openvcc;
eup->eu_closevcc = eni_closevcc;
eup->eu_output = eni_output;
eup->eu_vcc_zone = eni_vcc_zone;
eup->eu_nif_zone = eni_nif_zone;
/*
* Enable Memory Mapping / Bus Mastering
*/
val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
val |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, val);
/*
* Map in adapter RAM
*/
val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
if ((val & PCIM_CMD_MEMEN) == 0) {
log(LOG_ERR, "%s%d: memory mapping not enabled\n",
ENI_DEV_NAME, unit);
goto failed;
}
if ( ( pci_map_mem ( config_id, PCI_MAP_REG_START, &va, &pa ) ) == 0 )
{
log(LOG_ERR, "%s%d: unable to map memory\n",
ENI_DEV_NAME, unit);
goto failed;
}
/*
* Map okay - retain address assigned
*/
eup->eu_base = (Eni_mem)va;
eup->eu_ram = (Eni_mem)(eup->eu_base + RAM_OFFSET);
/*
* Map memory structures into adapter space
*/
eup->eu_suni = (Eni_mem)(eup->eu_base + SUNI_OFFSET);
eup->eu_midway = (Eni_mem)(eup->eu_base + MIDWAY_OFFSET);
eup->eu_vcitbl = (VCI_Table *)(eup->eu_base + VCITBL_OFFSET);
eup->eu_rxdma = (Eni_mem)(eup->eu_base + RXQUEUE_OFFSET);
eup->eu_txdma = (Eni_mem)(eup->eu_base + TXQUEUE_OFFSET);
eup->eu_svclist = (Eni_mem)(eup->eu_base + SVCLIST_OFFSET);
eup->eu_servread = 0;
/*
* Reset the midway chip
*/
eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET;
/*
* Size and test adapter memory. Initialize our adapter memory
* allocater.
*/
if ( eni_init_memory ( eup ) < 0 ) {
/*
* Adapter memory test failed. Clean up and
* return.
*/
log(LOG_ERR, "%s%d: memory test failed\n",
ENI_DEV_NAME, unit);
goto failed;
}
/*
* Read the contents of the SEEPROM
*/
eni_read_seeprom ( eup );
/*
* Copy MAC address to PIF and config structures
*/
bcopy ( (caddr_t)&eup->eu_seeprom[SEPROM_MAC_OFF],
(caddr_t)&eup->eu_pif.pif_macaddr, sizeof(struct mac_addr) );
eup->eu_config.ac_macaddr = eup->eu_pif.pif_macaddr;
/*
* Copy serial number into config space
*/
eup->eu_config.ac_serial =
ntohl(*(u_long *)&eup->eu_seeprom[SEPROM_SN_OFF]);
/*
* Convert Endianess on DMA
*/
val = pci_conf_read ( config_id, PCI_CONTROL_REG );
val |= ENDIAN_SWAP_DMA;
pci_conf_write ( config_id, PCI_CONTROL_REG, val );
/*
* Map interrupt in
*/
if ( !pci_map_int ( config_id, eni_intr, (void *)eup, &net_imask ) )
{
log(LOG_ERR, "%s%d: unable to map interrupt\n",
ENI_DEV_NAME, unit);
goto failed;
}
/*
* Setup some of the adapter configuration
*/
/*
* Get MIDWAY ID
*/
val = eup->eu_midway[MIDWAY_ID];
eup->eu_config.ac_vendor = VENDOR_ENI;
eup->eu_config.ac_vendapi = VENDAPI_ENI_1;
eup->eu_config.ac_device = DEV_ENI_155P;
eup->eu_config.ac_media = val & MEDIA_MASK ? MEDIA_UTP155 : MEDIA_OC3C;
eup->eu_pif.pif_pcr = ATM_PCR_OC3C;
eup->eu_config.ac_bustype = BUS_PCI;
eup->eu_config.ac_busslot = config_id->bus << 8 | config_id->slot;
/*
* Make a hw version number from the ID register values.
* Format: {Midway ID}.{Mother board ID}.{Daughter board ID}
*/
snprintf ( eup->eu_config.ac_hard_vers,
sizeof ( eup->eu_config.ac_hard_vers ),
"%ld/%ld/%ld",
(val >> ID_SHIFT) & ID_MASK,
(val >> MID_SHIFT) & MID_MASK,
(val >> DID_SHIFT) & DID_MASK );
/*
* There is no software version number
*/
eup->eu_config.ac_firm_vers[0] = '\0';
/*
* Save device ram info for user-level programs
* NOTE: This really points to start of EEPROM
* and includes all the device registers in the
* lower 2 Megabytes.
*/
eup->eu_config.ac_ram = (long)eup->eu_base;
eup->eu_config.ac_ramsize = eup->eu_ramsize + ENI_REG_SIZE;
/*
* Setup max VPI/VCI values
*/
eup->eu_pif.pif_maxvpi = ENI_MAX_VPI;
eup->eu_pif.pif_maxvci = ENI_MAX_VCI;
/*
* Register this interface with ATM core services
*/
if ( atm_physif_register
( (Cmn_unit *)eup, ENI_DEV_NAME, eni_services ) != 0 )
{
/*
* Registration failed - back everything out
*/
log(LOG_ERR, "%s%d: atm_physif_register failed\n",
ENI_DEV_NAME, unit);
goto failed;
}
eni_units[unit] = eup;
#if BSD >= 199506
/*
* Add hook to out shutdown function
*/
EVENTHANDLER_REGISTER(shutdown_post_sync, eni_pci_shutdown, eup,
SHUTDOWN_PRI_DEFAULT);
#endif
/*
* Initialize driver processing
*/
if ( eni_init ( eup ) ) {
log(LOG_ERR, "%s%d: adapter init failed\n",
ENI_DEV_NAME, unit);
goto failed;
}
return;
failed:
/*
* Attach failed - clean up
*/
eni_pci_reset(eup);
(void) pci_unmap_int(config_id);
atm_dev_free(eup);
return;
}
/*
* Device reset routine
*
* Arguments:
* eup pointer to per unit structure
*
* Returns:
* none
*
*/
static void
eni_pci_reset ( eup )
Eni_unit *eup;
{
/*
* We should really close down any open VCI's and
* release all memory (TX and RX) buffers. For now,
* we assume we're shutting the card down for good.
*/
if (eup->eu_midway) {
/*
* Issue RESET command to Midway chip
*/
eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET;
/*
* Delay to allow everything to terminate
*/
DELAY ( MIDWAY_DELAY );
}
return;
}
#if BSD < 199506
/*
* Device shutdown routine
*
* Arguments:
* kdc pointer to device's configuration table
* force forced shutdown flag
*
* Returns:
* none
*
*/
static int
eni_pci_shutdown ( kdc, force )
struct kern_devconf *kdc;
int force;
{
Eni_unit *eup = NULL;
if ( kdc->kdc_unit < eni_nunits ) {
eup = eni_units[kdc->kdc_unit];
if ( eup != NULL ) {
/* Do device reset */
eni_pci_reset ( eup );
}
}
(void) dev_detach ( kdc );
return ( 0 );
}
#else
/*
* Device shutdown routine
*
* Arguments:
* howto type of shutdown
* eup pointer to device unit structure
*
* Returns:
* none
*
*/
static void
eni_pci_shutdown ( eup, howto )
void *eup;
int howto;
{
/* Do device reset */
eni_pci_reset ( eup );
}
#endif /* BSD < 199506 */
#endif