1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-24 11:29:10 +00:00
freebsd/sys/dev/hea/eni.c
Peter Wemm 579f45fa60 Simplify the COMPAT_PCI_DRIVER/DATA_SET hack. We can add:
#define COMPAT_PCI_DRIVER(name,data) DATA_SET(pcidevice_set,data)
.. to 2.2.x and 3.x if people think it's worth it.  Driver writers can do
this if it's not defined.  (The reason for this is that I'm trying to
progressively eliminate use of linker_sets where it hurts modularity and
runtime load capability, and these DATA_SET's keep getting in the way.)
1999-05-09 17:07:30 +00:00

667 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.
*
* @(#) $Id: eni.c,v 1.6 1999/04/24 20:17:05 peter Exp $
*
*/
/*
* Efficient ENI adapter support
* -----------------------------
*
* Module supports PCI interface to ENI adapter
*
*/
#include <netatm/kern_include.h>
#include <dev/hea/eni_stats.h>
#include <dev/hea/eni.h>
#include <dev/hea/eni_var.h>
#ifndef lint
__RCSID("@(#) $Id: eni.c,v 1.6 1999/04/24 20:17:05 peter Exp $");
#endif
/*
* Typedef local functions
*/
static const char *eni_pci_probe __P((pcici_t, pcidi_t));
static void eni_pci_attach __P((pcici_t, int));
static int eni_get_ack __P((Eni_unit *));
static int eni_get_sebyte __P((Eni_unit *));
static void eni_read_seeprom __P((Eni_unit *));
#ifdef __FreeBSD__
#if BSD < 199506
static int eni_pci_shutdown __P((struct kern_devconf *, int));
#else
static void eni_pci_shutdown __P((int, void *));
#endif
static void eni_pci_reset __P((Eni_unit *));
#endif /* __FreeBSD__ */
/*
* Used by kernel to return number of claimed devices
*/
#ifdef __FreeBSD__
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);
#endif /* __FreeBSD__ */
/*
* 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 );
}
/*
* 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_conf_write ( eup->eu_pcitag, SEEPROM, \
eup->eu_sevar ); \
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_conf_read ( eup->eu_pcitag, SEEPROM );
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_conf_read ( eup->eu_pcitag, SEEPROM );
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
*
*/
static 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;
}
/*
* 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_pool = &eni_vcc_pool;
eup->eu_nif_pool = &eni_nif_pool;
/*
* Map in adapter RAM
*/
if ( ( pci_map_mem ( config_id, PCI_MAP_REG_START, &va, &pa ) ) == 0 )
{
/*
* Nothing's happened yet that we need to undo.
*/
return;
}
/*
* 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.
*/
/*
* FreeBSD doesn't support unmapping PCI memory (yet?).
*/
return;
}
/*
* Read the contents of the SEEPROM
*/
eni_read_seeprom ( eup );
/*
* Copy MAC address to PIF and config structures
*/
KM_COPY ( (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 ) )
{
/*
* Finish by unmapping memory, etc.
*/
log ( LOG_ERR, "eni_pci_attach: Unable to map interrupt\n" );
/*
* Can't unmap PCI memory (yet).
*/
return;
}
/*
* 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
*/
/*
* Can't unmap PCI memory (yet).
*/
/*
* Unmap PCI interrupt
*/
(void) pci_unmap_int ( config_id );
log ( LOG_ERR,
"eni_pci_attach: atm_physif_register failed\n" );
return;
}
eni_units[unit] = eup;
#if BSD >= 199506
/*
* Add hook to out shutdown function
*/
at_shutdown ( (bootlist_fn)eni_pci_shutdown, eup, SHUTDOWN_POST_SYNC );
#endif
/*
* Initialize driver processing
*/
if ( eni_init ( eup ) ) {
log ( LOG_ERR, "eni_pci_attach: Failed eni_init()\n" );
/*
* Can't unmap PCI memory (yet).
*/
/*
* Unmap PCI interrupt
*/
(void) pci_unmap_int ( config_id );
/*
* Fall through to return
*/
}
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.
*/
/*
* Issue RESET command to Midway chip
*/
eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET;
/*
* Delay to allow everything to terminate
*/
DELAY ( MIDWAY_DELAY );
return;
}
#ifdef __FreeBSD__
#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 ( howto, eup )
int howto;
void *eup;
{
/* Do device reset */
eni_pci_reset ( eup );
}
#endif /* BSD < 199506 */
#endif /* __FreeBSD__ */