The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/atm/hea/eni.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  *
    3  * ===================================
    4  * HARP  |  Host ATM Research Platform
    5  * ===================================
    6  *
    7  *
    8  * This Host ATM Research Platform ("HARP") file (the "Software") is
    9  * made available by Network Computing Services, Inc. ("NetworkCS")
   10  * "AS IS".  NetworkCS does not provide maintenance, improvements or
   11  * support of any kind.
   12  *
   13  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
   14  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
   15  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
   16  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
   17  * In no event shall NetworkCS be responsible for any damages, including
   18  * but not limited to consequential damages, arising from or relating to
   19  * any use of the Software or related support.
   20  *
   21  * Copyright 1994-1998 Network Computing Services, Inc.
   22  *
   23  * Copies of this Software may be made, however, the above copyright
   24  * notice must be reproduced on all copies.
   25  *
   26  *      @(#) $FreeBSD: src/sys/dev/hea/eni.c,v 1.10 1999/08/28 00:41:42 peter Exp $
   27  *      @(#) $DragonFly: src/sys/dev/atm/hea/eni.c,v 1.11 2008/03/01 22:03:13 swildner Exp $
   28  */
   29 
   30 /*
   31  * Efficient ENI adapter support
   32  * -----------------------------
   33  *
   34  * Module supports PCI interface to ENI adapter
   35  *
   36  */
   37 
   38 #include <netproto/atm/kern_include.h>
   39 
   40 #include "eni_stats.h"
   41 #include "eni.h"
   42 #include "eni_var.h"
   43 
   44 /*
   45  * Typedef local functions
   46  */
   47 static const char       *eni_pci_probe (pcici_t, pcidi_t);
   48 static void     eni_pci_attach (pcici_t, int);
   49 static int      eni_get_ack (Eni_unit *);
   50 static int      eni_get_sebyte (Eni_unit *);
   51 static void     eni_read_seeprom (Eni_unit *);
   52 static void     eni_pci_shutdown (void *, int);
   53 static void     eni_pci_reset (Eni_unit *);
   54 
   55 /*
   56  * Used by kernel to return number of claimed devices
   57  */
   58 static u_long eni_nunits;
   59 
   60 static struct pci_device eni_pci_device = {
   61         ENI_DEV_NAME,
   62         eni_pci_probe,
   63         eni_pci_attach,
   64         &eni_nunits,
   65         NULL
   66 };
   67 
   68 COMPAT_PCI_DRIVER (eni_pci, eni_pci_device);
   69 
   70 /*
   71  * Called by kernel with PCI device_id which was read from the PCI
   72  * register set. If the identified vendor is Efficient, see if we
   73  * recognize the particular device. If so, return an identifying string,
   74  * if not, return null.
   75  *
   76  * Arguments:
   77  *      config_id       PCI config token
   78  *      device_id       contents of PCI device ID register
   79  *
   80  * Returns:
   81  *      string          Identifying string if we will handle this device
   82  *      NULL            unrecognized vendor/device
   83  *
   84  */
   85 static const char *
   86 eni_pci_probe ( pcici_t config_id, pcidi_t device_id )
   87 {
   88 
   89         if ( (device_id & 0xFFFF) == EFF_VENDOR_ID ) {
   90                 switch ( (device_id >> 16) ) {
   91                         case EFF_DEV_ID:
   92                                 return ( "Efficient ENI ATM Adapter" );
   93 /* NOTREACHED */
   94                                 break;
   95                 }
   96         }
   97 
   98         return ( NULL );
   99 }
  100 
  101 /*
  102  * The ENI-155p adapter uses an ATMEL AT24C01 serial EEPROM to store
  103  * configuration information. The SEEPROM is accessed via two wires,
  104  * CLOCK and DATA, which are accessible via the PCI configuration
  105  * registers. The following macros manipulate the lines to access the
  106  * SEEPROM. See http://www.atmel.com/atmel/products/prod162.htm for
  107  * a description of the AT24C01 part. Value to be read/written is
  108  * part of the per unit structure.
  109  */
  110 /*
  111  * Write bits to SEEPROM
  112  */
  113 #define WRITE_SEEPROM() (                                               \
  114     {                                                                   \
  115         (void) pci_conf_write ( eup->eu_pcitag, SEEPROM,                \
  116                 eup->eu_sevar );                                        \
  117         DELAY(SEPROM_DELAY);                                            \
  118     }                                                                   \
  119 )
  120 /*
  121  * Stobe first the DATA, then the CLK lines high
  122  */
  123 #define STROBE_HIGH()   (                                               \
  124     {                                                                   \
  125         eup->eu_sevar |= SEPROM_DATA; WRITE_SEEPROM();                  \
  126         eup->eu_sevar |= SEPROM_CLK;  WRITE_SEEPROM();                  \
  127     }                                                                   \
  128 )
  129 /*
  130  * Strobe first the CLK, then the DATA lines high
  131  */
  132 #define INV_STROBE_HIGH()       (                                       \
  133     {                                                                   \
  134         eup->eu_sevar |= SEPROM_CLK;  WRITE_SEEPROM();                  \
  135         eup->eu_sevar |= SEPROM_DATA; WRITE_SEEPROM();                  \
  136     }                                                                   \
  137 )
  138 /*
  139  * Strobe first the CLK, then the DATA lines low - companion to
  140  * STROBE_HIGH()
  141  */
  142 #define STROBE_LOW()    (                                               \
  143     {                                                                   \
  144         eup->eu_sevar &= ~SEPROM_CLK;  WRITE_SEEPROM();                 \
  145         eup->eu_sevar &= ~SEPROM_DATA; WRITE_SEEPROM();                 \
  146     }                                                                   \
  147 )
  148 /*
  149  * Strobe first the DATA, then the CLK lines low - companion to
  150  * INV_STROBE_HIGH()
  151  */
  152 #define INV_STROBE_LOW()        (                                       \
  153     {                                                                   \
  154         eup->eu_sevar &= ~SEPROM_DATA; WRITE_SEEPROM();                 \
  155         eup->eu_sevar &= ~SEPROM_CLK;  WRITE_SEEPROM();                 \
  156     }                                                                   \
  157 )
  158 /*
  159  * Strobe the CLK line high, then low
  160  */
  161 #define STROBE_CLK()    (                                               \
  162     {                                                                   \
  163         eup->eu_sevar |= SEPROM_CLK;   WRITE_SEEPROM();                 \
  164         eup->eu_sevar &= ~SEPROM_CLK;  WRITE_SEEPROM();                 \
  165     }                                                                   \
  166 )
  167 
  168 /*
  169  * Look for a positive ACK from the SEEPROM. Cycle begins by asserting
  170  * the DATA line, then the CLK line. The DATA line is then read to
  171  * retrieve the ACK status, and then the cycle is finished by deasserting
  172  * the CLK line, and asserting the DATA line.
  173  *
  174  * Arguments:
  175  *      eup             pointer to per unit structure
  176  *
  177  * Returns:
  178  *      0/1             value of ACK
  179  *
  180  */
  181 static int
  182 eni_get_ack(Eni_unit *eup)
  183 {
  184         int             ack;
  185 
  186         STROBE_HIGH();
  187         /*
  188          * Read DATA line from SEPROM
  189          */
  190         eup->eu_sevar = pci_conf_read ( eup->eu_pcitag, SEEPROM );
  191         DELAY ( SEPROM_DELAY );
  192         ack = eup->eu_sevar & SEPROM_DATA;
  193 
  194         eup->eu_sevar &= ~SEPROM_CLK;
  195         WRITE_SEEPROM ();
  196         eup->eu_sevar |= SEPROM_DATA;
  197         WRITE_SEEPROM ();
  198 
  199         return ( ack );
  200 }
  201 
  202 /*
  203  * Read a byte from the SEEPROM. Data is read as 8 bits. There are two types
  204  * of read operations. The first is a single byte read, the second is
  205  * multiple sequential bytes read. Both cycles begin with a 'START' operation,
  206  * followed by a memory address word. Following the memory address, the
  207  * SEEPROM will send a data byte, followed by an ACK. If the host responds
  208  * with a 'STOP' operation, then a single byte cycle is performed. If the
  209  * host responds with an 'ACK', then the memory address is incremented, and
  210  * the next sequential memory byte is serialized.
  211  *
  212  * Arguments:
  213  *      eup             pointer to per unit structure
  214  *
  215  * Returns:
  216  *      val             value of byte read from SEEPROM
  217  *
  218  */
  219 static int
  220 eni_get_sebyte(Eni_unit *eup)
  221 {
  222         int     i;
  223         int     data;
  224         int     rval;
  225 
  226         /* Initial value */
  227         rval = 0;
  228         /* Read 8 bits */
  229         for ( i = 0; i < 8; i++ ) {
  230                 /* Shift bits to left so the next bit goes to position 0 */
  231                 rval <<= 1;
  232                 /* Indicate we're ready to read bit */
  233                 STROBE_HIGH();
  234                 /*
  235                  * Read DATA line from SEPROM
  236                  */
  237                 data = pci_conf_read ( eup->eu_pcitag, SEEPROM );
  238                 DELAY ( SEPROM_DELAY );
  239                 /* (Possibly) mask bit into accumulating value */
  240                 if ( data & SEPROM_DATA )
  241                         rval |= 1;              /* If DATA bit '1' */
  242                 /* Indicate we're done reading this bit */
  243                 STROBE_LOW();
  244         }
  245 
  246         /* Return acquired byte */
  247         return ( rval );
  248 }
  249 
  250 /*
  251  * The AT24C01 is a 1024 bit part organized as 128 words by 8 bits.
  252  * We will read the entire contents into the per unit structure. Later,
  253  * we'll retrieve the MAC address and serial number from the data read.
  254  *
  255  * Arguments:
  256  *      eup             pointer to per unit structure
  257  *
  258  * Returns:
  259  *      none
  260  *
  261  */
  262 static void
  263 eni_read_seeprom(Eni_unit *eup)
  264 {
  265         int     addr;
  266         int     i, j;
  267 
  268         /*
  269          * Set initial state
  270          */
  271         eup->eu_sevar = SEPROM_DATA | SEPROM_CLK;
  272         WRITE_SEEPROM ();
  273 
  274         /* Loop for all bytes */
  275         for ( i = 0; i < SEPROM_SIZE ; i++ ) {
  276                 /* Send START operation */
  277                 STROBE_HIGH();
  278                 INV_STROBE_LOW();
  279 
  280                 /*
  281                  * Send address. Addresses are sent as 7 bits plus
  282                  * last bit high.
  283                  */
  284                 addr = ((i) << 1) + 1;
  285                 /*
  286                  * Start with high order bit first working toward low
  287                  * order bit.
  288                  */
  289                 for ( j = 7; j >= 0; j-- ) {
  290                         /* Set current bit value */
  291                         eup->eu_sevar = ( addr >> j ) & 1 ?
  292                             eup->eu_sevar | SEPROM_DATA :
  293                                 eup->eu_sevar & ~SEPROM_DATA;
  294                         WRITE_SEEPROM ();
  295                         /* Indicate we've sent it */
  296                         STROBE_CLK();
  297                 }
  298                 /*
  299                  * We expect a zero ACK after sending the address
  300                  */
  301                 if ( !eni_get_ack ( eup ) ) {
  302                         /* Address okay - read data byte */
  303                         eup->eu_seeprom[i] = eni_get_sebyte ( eup );
  304                         /* Grab but ignore the ACK op */
  305                         (void) eni_get_ack ( eup );
  306                 } else {
  307                         /* Address ACK was bad - can't retrieve data byte */
  308                         eup->eu_seeprom[i] = 0xff;
  309                 }
  310         }
  311 
  312         return;
  313 }
  314 
  315 /*
  316  * The kernel has found a device which we are willing to support.
  317  * We are now being called to do any necessary work to make the
  318  * device initially usable. In our case, this means allocating
  319  * structure memory, configuring registers, mapping device
  320  * memory, setting pointers, registering with the core services,
  321  * and doing the initial PDU processing configuration.
  322  *
  323  * Arguments:
  324  *      config_id               PCI device token
  325  *      unit                    instance of the unit
  326  *
  327  * Returns:
  328  *      none            
  329  *
  330  */
  331 static void
  332 eni_pci_attach ( pcici_t config_id, int unit )
  333 {
  334         vm_offset_t     va;
  335         vm_offset_t     pa;
  336         Eni_unit        *eup;
  337         long            val;
  338 
  339         /*
  340          * Just checking...
  341          */
  342         if ( unit >= ENI_MAX_UNITS ) {
  343                 log ( LOG_ERR, "%s%d: too many devices\n",
  344                         ENI_DEV_NAME, unit );
  345                 return;
  346         }
  347 
  348         /*
  349          * Make sure this isn't a duplicate unit
  350          */
  351         if ( eni_units[unit] != NULL )
  352                 return;
  353 
  354         /*
  355          * Allocate a new unit structure
  356          */
  357         eup = (Eni_unit *) atm_dev_alloc ( sizeof(Eni_unit), sizeof(int), 0 );
  358         if ( eup == NULL )
  359                 return;
  360 
  361         /*
  362          * Start initializing it
  363          */
  364         eup->eu_unit = unit;
  365         eup->eu_mtu = ENI_IFF_MTU;
  366         eup->eu_pcitag = config_id;
  367         eup->eu_ioctl = eni_atm_ioctl;
  368         eup->eu_instvcc = eni_instvcc;
  369         eup->eu_openvcc = eni_openvcc;
  370         eup->eu_closevcc = eni_closevcc;
  371         eup->eu_output = eni_output;
  372         eup->eu_vcc_pool = &eni_vcc_pool;
  373         eup->eu_nif_pool = &eni_nif_pool;
  374 
  375         /*
  376          * Enable Memory Mapping / Bus Mastering 
  377          */
  378         val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
  379         val |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
  380         pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, val);
  381 
  382         /*
  383          * Map in adapter RAM
  384          */
  385         val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
  386         if ((val & PCIM_CMD_MEMEN) == 0) {
  387                 log(LOG_ERR, "%s%d: memory mapping not enabled\n", 
  388                         ENI_DEV_NAME, unit);
  389                 goto failed;
  390         }
  391         if ( ( pci_map_mem ( config_id, PCI_MAP_REG_START, &va, &pa ) ) == 0 )
  392         {
  393                 log(LOG_ERR, "%s%d: unable to map memory\n", 
  394                         ENI_DEV_NAME, unit);
  395                 goto failed;
  396         }
  397         /*
  398          * Map okay - retain address assigned
  399          */
  400         eup->eu_base = (Eni_mem)va;
  401         eup->eu_ram = (Eni_mem)(eup->eu_base + RAM_OFFSET);
  402 
  403         /*
  404          * Map memory structures into adapter space
  405          */
  406         eup->eu_suni = (Eni_mem)(eup->eu_base + SUNI_OFFSET);
  407         eup->eu_midway = (Eni_mem)(eup->eu_base + MIDWAY_OFFSET);
  408         eup->eu_vcitbl = (VCI_Table *)(eup->eu_base + VCITBL_OFFSET);
  409         eup->eu_rxdma = (Eni_mem)(eup->eu_base + RXQUEUE_OFFSET);
  410         eup->eu_txdma = (Eni_mem)(eup->eu_base + TXQUEUE_OFFSET);
  411         eup->eu_svclist = (Eni_mem)(eup->eu_base + SVCLIST_OFFSET);
  412         eup->eu_servread = 0;
  413 
  414         /*
  415          * Reset the midway chip
  416          */
  417         eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET;
  418 
  419         /*
  420          * Size and test adapter memory. Initialize our adapter memory
  421          * allocater.
  422          */
  423         if ( eni_init_memory ( eup ) < 0 ) {
  424                 /*
  425                  * Adapter memory test failed. Clean up and
  426                  * return.
  427                  */
  428                 log(LOG_ERR, "%s%d: memory test failed\n", 
  429                         ENI_DEV_NAME, unit);
  430                 goto failed;
  431         }
  432 
  433         /*
  434          * Read the contents of the SEEPROM
  435          */
  436         eni_read_seeprom ( eup );
  437         /*
  438          * Copy MAC address to PIF and config structures
  439          */
  440         KM_COPY ( (caddr_t)&eup->eu_seeprom[SEPROM_MAC_OFF],
  441             (caddr_t)&eup->eu_pif.pif_macaddr, sizeof(struct mac_addr) );
  442         eup->eu_config.ac_macaddr = eup->eu_pif.pif_macaddr;
  443 
  444         /*
  445          * Copy serial number into config space
  446          */
  447         eup->eu_config.ac_serial =
  448                 ntohl(*(u_long *)&eup->eu_seeprom[SEPROM_SN_OFF]);
  449 
  450         /*
  451          * Convert Endianess on DMA
  452          */
  453         val = pci_conf_read ( config_id, PCI_CONTROL_REG );
  454         val |= ENDIAN_SWAP_DMA;
  455         pci_conf_write ( config_id, PCI_CONTROL_REG, val );
  456 
  457         /*
  458          * Map interrupt in
  459          */
  460         if ( !pci_map_int ( config_id, eni_intr, (void *)eup) )
  461         {
  462                 log(LOG_ERR, "%s%d: unable to map interrupt\n", 
  463                         ENI_DEV_NAME, unit);
  464                 goto failed;
  465         }
  466 
  467         /*
  468          * Setup some of the adapter configuration
  469          */
  470         /*
  471          * Get MIDWAY ID
  472          */
  473         val = eup->eu_midway[MIDWAY_ID];
  474         eup->eu_config.ac_vendor = VENDOR_ENI;
  475         eup->eu_config.ac_vendapi = VENDAPI_ENI_1;
  476         eup->eu_config.ac_device = DEV_ENI_155P;
  477         eup->eu_config.ac_media = val & MEDIA_MASK ? MEDIA_UTP155 : MEDIA_OC3C;
  478         eup->eu_pif.pif_pcr = ATM_PCR_OC3C;
  479         eup->eu_config.ac_bustype = BUS_PCI;
  480         eup->eu_config.ac_busslot = config_id->bus << 8 | config_id->slot;
  481 
  482         /*
  483          * Make a hw version number from the ID register values.
  484          * Format: {Midway ID}.{Mother board ID}.{Daughter board ID}
  485          */
  486         ksnprintf ( eup->eu_config.ac_hard_vers,
  487             sizeof ( eup->eu_config.ac_hard_vers ),
  488                 "%ld/%ld/%ld",
  489                 (val >> ID_SHIFT) & ID_MASK,
  490                 (val >> MID_SHIFT) & MID_MASK,
  491                 (val >> DID_SHIFT) & DID_MASK );
  492 
  493         /*
  494          * There is no software version number
  495          */
  496         eup->eu_config.ac_firm_vers[0] = '\0';
  497 
  498         /*
  499          * Save device ram info for user-level programs
  500          * NOTE: This really points to start of EEPROM
  501          * and includes all the device registers in the
  502          * lower 2 Megabytes.
  503          */
  504         eup->eu_config.ac_ram = (long)eup->eu_base;
  505         eup->eu_config.ac_ramsize = eup->eu_ramsize + ENI_REG_SIZE;
  506 
  507         /*
  508          * Setup max VPI/VCI values
  509          */
  510         eup->eu_pif.pif_maxvpi = ENI_MAX_VPI;
  511         eup->eu_pif.pif_maxvci = ENI_MAX_VCI;
  512 
  513         /*
  514          * Register this interface with ATM core services
  515          */
  516         if ( atm_physif_register
  517                 ( (Cmn_unit *)eup, ENI_DEV_NAME, eni_services ) != 0 )
  518         {
  519                 /*
  520                  * Registration failed - back everything out
  521                  */
  522                 log(LOG_ERR, "%s%d: atm_physif_register failed\n", 
  523                         ENI_DEV_NAME, unit);
  524                 goto failed;
  525         }
  526 
  527         eni_units[unit] = eup;
  528 
  529         /*
  530          * Add hook to out shutdown function
  531          */
  532         EVENTHANDLER_REGISTER(shutdown_post_sync, eni_pci_shutdown, eup,
  533                               SHUTDOWN_PRI_DRIVER);
  534 
  535         /*
  536          * Initialize driver processing
  537          */
  538         if ( eni_init ( eup ) ) {
  539                 log(LOG_ERR, "%s%d: adapter init failed\n", 
  540                         ENI_DEV_NAME, unit);
  541                 goto failed;
  542         }
  543 
  544         return;
  545 
  546 failed:
  547         /*
  548          * Attach failed - clean up
  549          */
  550         eni_pci_reset(eup);
  551         (void) pci_unmap_int(config_id);
  552         atm_dev_free(eup);
  553         return;
  554 }
  555 
  556 /*
  557  * Device reset routine
  558  *
  559  * Arguments:
  560  *      eup                     pointer to per unit structure
  561  *
  562  * Returns:
  563  *      none
  564  *
  565  */
  566 static void
  567 eni_pci_reset(Eni_unit *eup)
  568 {
  569 
  570         /*
  571          * We should really close down any open VCI's and
  572          * release all memory (TX and RX) buffers. For now,
  573          * we assume we're shutting the card down for good.
  574          */
  575 
  576         if (eup->eu_midway) {
  577                 /*
  578                  * Issue RESET command to Midway chip
  579                  */
  580                 eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET;
  581 
  582                 /*
  583                  * Delay to allow everything to terminate
  584                  */
  585                 DELAY ( MIDWAY_DELAY );
  586         }
  587 
  588         return;
  589 }
  590 
  591 /*
  592  * Device shutdown routine
  593  *
  594  * Arguments:
  595  *      howto           type of shutdown
  596  *      eup             pointer to device unit structure
  597  *
  598  * Returns:
  599  *      none
  600  *
  601  */
  602 static void
  603 eni_pci_shutdown (void *eup, int howto)
  604 {
  605 
  606         /* Do device reset */
  607         eni_pci_reset ( eup );
  608 
  609 }

Cache object: 3ffa264af6e5d4aa3028c672a9f55272


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.