diff options
Diffstat (limited to 'sys/dev/hea/eni.c')
-rw-r--r-- | sys/dev/hea/eni.c | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/sys/dev/hea/eni.c b/sys/dev/hea/eni.c new file mode 100644 index 000000000000..088503aa1993 --- /dev/null +++ b/sys/dev/hea/eni.c @@ -0,0 +1,664 @@ +/* + * + * =================================== + * 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.4 1998/06/29 19:39:10 jpt Exp $ + * + */ + +/* + * Efficient ENI adapter support + * ----------------------------- + * + * Module supports PCI interface to ENI adapter + * + */ + +#ifndef lint +static char *RCSid = "@(#) $Id: eni.c,v 1.4 1998/06/29 19:39:10 jpt Exp $"; +#endif + +#include <netatm/kern_include.h> + +#include <dev/hea/eni_stats.h> +#include <dev/hea/eni.h> +#include <dev/hea/eni_var.h> + +/* + * Typedef local functions + */ +static 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 +}; + +DATA_SET ( pcidevice_set, 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 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} + */ + sprintf ( eup->eu_config.ac_hard_vers, "%d/%d/%d", + (val >> ID_SHIFT) & ID_MASK, + (val >> MID_SHIFT) & MID_MASK, + (val >> DID_SHIFT) & DID_MASK ); + + /* + * There is no software version number + */ + sprintf ( eup->eu_config.ac_firm_vers, "\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__ */ |