Merge from vendor branch BSDTAR:
[dragonfly.git] / sys / dev / atm / hea / eni.c
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.7 2005/02/01 00:51:49 joerg 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 ( eup )
183         Eni_unit        *eup;
184 {
185         int             ack;
186
187         STROBE_HIGH();
188         /*
189          * Read DATA line from SEPROM
190          */
191         eup->eu_sevar = pci_conf_read ( eup->eu_pcitag, SEEPROM );
192         DELAY ( SEPROM_DELAY );
193         ack = eup->eu_sevar & SEPROM_DATA;
194
195         eup->eu_sevar &= ~SEPROM_CLK;
196         WRITE_SEEPROM ();
197         eup->eu_sevar |= SEPROM_DATA;
198         WRITE_SEEPROM ();
199
200         return ( ack );
201 }
202
203 /*
204  * Read a byte from the SEEPROM. Data is read as 8 bits. There are two types
205  * of read operations. The first is a single byte read, the second is
206  * multiple sequential bytes read. Both cycles begin with a 'START' operation,
207  * followed by a memory address word. Following the memory address, the
208  * SEEPROM will send a data byte, followed by an ACK. If the host responds
209  * with a 'STOP' operation, then a single byte cycle is performed. If the
210  * host responds with an 'ACK', then the memory address is incremented, and
211  * the next sequential memory byte is serialized.
212  *
213  * Arguments:
214  *      eup             pointer to per unit structure
215  *
216  * Returns:
217  *      val             value of byte read from SEEPROM
218  *
219  */
220 static int
221 eni_get_sebyte( eup )
222         Eni_unit        *eup;
223 {
224         int     i;
225         int     data;
226         int     rval;
227
228         /* Initial value */
229         rval = 0;
230         /* Read 8 bits */
231         for ( i = 0; i < 8; i++ ) {
232                 /* Shift bits to left so the next bit goes to position 0 */
233                 rval <<= 1;
234                 /* Indicate we're ready to read bit */
235                 STROBE_HIGH();
236                 /*
237                  * Read DATA line from SEPROM
238                  */
239                 data = pci_conf_read ( eup->eu_pcitag, SEEPROM );
240                 DELAY ( SEPROM_DELAY );
241                 /* (Possibly) mask bit into accumulating value */
242                 if ( data & SEPROM_DATA )
243                         rval |= 1;              /* If DATA bit '1' */
244                 /* Indicate we're done reading this bit */
245                 STROBE_LOW();
246         }
247
248         /* Return acquired byte */
249         return ( rval );
250 }
251
252 /*
253  * The AT24C01 is a 1024 bit part organized as 128 words by 8 bits.
254  * We will read the entire contents into the per unit structure. Later,
255  * we'll retrieve the MAC address and serial number from the data read.
256  *
257  * Arguments:
258  *      eup             pointer to per unit structure
259  *
260  * Returns:
261  *      none
262  *
263  */
264 static void
265 eni_read_seeprom ( eup )
266         Eni_unit        *eup;
267 {
268         int     addr;
269         int     i, j;
270
271         /*
272          * Set initial state
273          */
274         eup->eu_sevar = SEPROM_DATA | SEPROM_CLK;
275         WRITE_SEEPROM ();
276
277         /* Loop for all bytes */
278         for ( i = 0; i < SEPROM_SIZE ; i++ ) {
279                 /* Send START operation */
280                 STROBE_HIGH();
281                 INV_STROBE_LOW();
282
283                 /*
284                  * Send address. Addresses are sent as 7 bits plus
285                  * last bit high.
286                  */
287                 addr = ((i) << 1) + 1;
288                 /*
289                  * Start with high order bit first working toward low
290                  * order bit.
291                  */
292                 for ( j = 7; j >= 0; j-- ) {
293                         /* Set current bit value */
294                         eup->eu_sevar = ( addr >> j ) & 1 ?
295                             eup->eu_sevar | SEPROM_DATA :
296                                 eup->eu_sevar & ~SEPROM_DATA;
297                         WRITE_SEEPROM ();
298                         /* Indicate we've sent it */
299                         STROBE_CLK();
300                 }
301                 /*
302                  * We expect a zero ACK after sending the address
303                  */
304                 if ( !eni_get_ack ( eup ) ) {
305                         /* Address okay - read data byte */
306                         eup->eu_seeprom[i] = eni_get_sebyte ( eup );
307                         /* Grab but ignore the ACK op */
308                         (void) eni_get_ack ( eup );
309                 } else {
310                         /* Address ACK was bad - can't retrieve data byte */
311                         eup->eu_seeprom[i] = 0xff;
312                 }
313         }
314
315         return;
316 }
317
318 /*
319  * The kernel has found a device which we are willing to support.
320  * We are now being called to do any necessary work to make the
321  * device initially usable. In our case, this means allocating
322  * structure memory, configuring registers, mapping device
323  * memory, setting pointers, registering with the core services,
324  * and doing the initial PDU processing configuration.
325  *
326  * Arguments:
327  *      config_id               PCI device token
328  *      unit                    instance of the unit
329  *
330  * Returns:
331  *      none            
332  *
333  */
334 static void
335 eni_pci_attach ( pcici_t config_id, int unit )
336 {
337         vm_offset_t     va;
338         vm_offset_t     pa;
339         Eni_unit        *eup;
340         long            val;
341
342         /*
343          * Just checking...
344          */
345         if ( unit >= ENI_MAX_UNITS ) {
346                 log ( LOG_ERR, "%s%d: too many devices\n",
347                         ENI_DEV_NAME, unit );
348                 return;
349         }
350
351         /*
352          * Make sure this isn't a duplicate unit
353          */
354         if ( eni_units[unit] != NULL )
355                 return;
356
357         /*
358          * Allocate a new unit structure
359          */
360         eup = (Eni_unit *) atm_dev_alloc ( sizeof(Eni_unit), sizeof(int), 0 );
361         if ( eup == NULL )
362                 return;
363
364         /*
365          * Start initializing it
366          */
367         eup->eu_unit = unit;
368         eup->eu_mtu = ENI_IFF_MTU;
369         eup->eu_pcitag = config_id;
370         eup->eu_ioctl = eni_atm_ioctl;
371         eup->eu_instvcc = eni_instvcc;
372         eup->eu_openvcc = eni_openvcc;
373         eup->eu_closevcc = eni_closevcc;
374         eup->eu_output = eni_output;
375         eup->eu_vcc_pool = &eni_vcc_pool;
376         eup->eu_nif_pool = &eni_nif_pool;
377
378         /*
379          * Enable Memory Mapping / Bus Mastering 
380          */
381         val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
382         val |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
383         pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, val);
384
385         /*
386          * Map in adapter RAM
387          */
388         val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
389         if ((val & PCIM_CMD_MEMEN) == 0) {
390                 log(LOG_ERR, "%s%d: memory mapping not enabled\n", 
391                         ENI_DEV_NAME, unit);
392                 goto failed;
393         }
394         if ( ( pci_map_mem ( config_id, PCI_MAP_REG_START, &va, &pa ) ) == 0 )
395         {
396                 log(LOG_ERR, "%s%d: unable to map memory\n", 
397                         ENI_DEV_NAME, unit);
398                 goto failed;
399         }
400         /*
401          * Map okay - retain address assigned
402          */
403         eup->eu_base = (Eni_mem)va;
404         eup->eu_ram = (Eni_mem)(eup->eu_base + RAM_OFFSET);
405
406         /*
407          * Map memory structures into adapter space
408          */
409         eup->eu_suni = (Eni_mem)(eup->eu_base + SUNI_OFFSET);
410         eup->eu_midway = (Eni_mem)(eup->eu_base + MIDWAY_OFFSET);
411         eup->eu_vcitbl = (VCI_Table *)(eup->eu_base + VCITBL_OFFSET);
412         eup->eu_rxdma = (Eni_mem)(eup->eu_base + RXQUEUE_OFFSET);
413         eup->eu_txdma = (Eni_mem)(eup->eu_base + TXQUEUE_OFFSET);
414         eup->eu_svclist = (Eni_mem)(eup->eu_base + SVCLIST_OFFSET);
415         eup->eu_servread = 0;
416
417         /*
418          * Reset the midway chip
419          */
420         eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET;
421
422         /*
423          * Size and test adapter memory. Initialize our adapter memory
424          * allocater.
425          */
426         if ( eni_init_memory ( eup ) < 0 ) {
427                 /*
428                  * Adapter memory test failed. Clean up and
429                  * return.
430                  */
431                 log(LOG_ERR, "%s%d: memory test failed\n", 
432                         ENI_DEV_NAME, unit);
433                 goto failed;
434         }
435
436         /*
437          * Read the contents of the SEEPROM
438          */
439         eni_read_seeprom ( eup );
440         /*
441          * Copy MAC address to PIF and config structures
442          */
443         KM_COPY ( (caddr_t)&eup->eu_seeprom[SEPROM_MAC_OFF],
444             (caddr_t)&eup->eu_pif.pif_macaddr, sizeof(struct mac_addr) );
445         eup->eu_config.ac_macaddr = eup->eu_pif.pif_macaddr;
446
447         /*
448          * Copy serial number into config space
449          */
450         eup->eu_config.ac_serial =
451                 ntohl(*(u_long *)&eup->eu_seeprom[SEPROM_SN_OFF]);
452
453         /*
454          * Convert Endianess on DMA
455          */
456         val = pci_conf_read ( config_id, PCI_CONTROL_REG );
457         val |= ENDIAN_SWAP_DMA;
458         pci_conf_write ( config_id, PCI_CONTROL_REG, val );
459
460         /*
461          * Map interrupt in
462          */
463         if ( !pci_map_int ( config_id, eni_intr, (void *)eup, &net_imask ) )
464         {
465                 log(LOG_ERR, "%s%d: unable to map interrupt\n", 
466                         ENI_DEV_NAME, unit);
467                 goto failed;
468         }
469
470         /*
471          * Setup some of the adapter configuration
472          */
473         /*
474          * Get MIDWAY ID
475          */
476         val = eup->eu_midway[MIDWAY_ID];
477         eup->eu_config.ac_vendor = VENDOR_ENI;
478         eup->eu_config.ac_vendapi = VENDAPI_ENI_1;
479         eup->eu_config.ac_device = DEV_ENI_155P;
480         eup->eu_config.ac_media = val & MEDIA_MASK ? MEDIA_UTP155 : MEDIA_OC3C;
481         eup->eu_pif.pif_pcr = ATM_PCR_OC3C;
482         eup->eu_config.ac_bustype = BUS_PCI;
483         eup->eu_config.ac_busslot = config_id->bus << 8 | config_id->slot;
484
485         /*
486          * Make a hw version number from the ID register values.
487          * Format: {Midway ID}.{Mother board ID}.{Daughter board ID}
488          */
489         snprintf ( eup->eu_config.ac_hard_vers,
490             sizeof ( eup->eu_config.ac_hard_vers ),
491                 "%ld/%ld/%ld",
492                 (val >> ID_SHIFT) & ID_MASK,
493                 (val >> MID_SHIFT) & MID_MASK,
494                 (val >> DID_SHIFT) & DID_MASK );
495
496         /*
497          * There is no software version number
498          */
499         eup->eu_config.ac_firm_vers[0] = '\0';
500
501         /*
502          * Save device ram info for user-level programs
503          * NOTE: This really points to start of EEPROM
504          * and includes all the device registers in the
505          * lower 2 Megabytes.
506          */
507         eup->eu_config.ac_ram = (long)eup->eu_base;
508         eup->eu_config.ac_ramsize = eup->eu_ramsize + ENI_REG_SIZE;
509
510         /*
511          * Setup max VPI/VCI values
512          */
513         eup->eu_pif.pif_maxvpi = ENI_MAX_VPI;
514         eup->eu_pif.pif_maxvci = ENI_MAX_VCI;
515
516         /*
517          * Register this interface with ATM core services
518          */
519         if ( atm_physif_register
520                 ( (Cmn_unit *)eup, ENI_DEV_NAME, eni_services ) != 0 )
521         {
522                 /*
523                  * Registration failed - back everything out
524                  */
525                 log(LOG_ERR, "%s%d: atm_physif_register failed\n", 
526                         ENI_DEV_NAME, unit);
527                 goto failed;
528         }
529
530         eni_units[unit] = eup;
531
532         /*
533          * Add hook to out shutdown function
534          */
535         EVENTHANDLER_REGISTER(shutdown_post_sync, eni_pci_shutdown, eup,
536                               SHUTDOWN_PRI_DEFAULT);
537
538         /*
539          * Initialize driver processing
540          */
541         if ( eni_init ( eup ) ) {
542                 log(LOG_ERR, "%s%d: adapter init failed\n", 
543                         ENI_DEV_NAME, unit);
544                 goto failed;
545         }
546
547         return;
548
549 failed:
550         /*
551          * Attach failed - clean up
552          */
553         eni_pci_reset(eup);
554         (void) pci_unmap_int(config_id);
555         atm_dev_free(eup);
556         return;
557 }
558
559 /*
560  * Device reset routine
561  *
562  * Arguments:
563  *      eup                     pointer to per unit structure
564  *
565  * Returns:
566  *      none
567  *
568  */
569 static void
570 eni_pci_reset ( eup )
571         Eni_unit *eup;
572 {
573
574         /*
575          * We should really close down any open VCI's and
576          * release all memory (TX and RX) buffers. For now,
577          * we assume we're shutting the card down for good.
578          */
579
580         if (eup->eu_midway) {
581                 /*
582                  * Issue RESET command to Midway chip
583                  */
584                 eup->eu_midway[MIDWAY_ID] = MIDWAY_RESET;
585
586                 /*
587                  * Delay to allow everything to terminate
588                  */
589                 DELAY ( MIDWAY_DELAY );
590         }
591
592         return;
593 }
594
595 /*
596  * Device shutdown routine
597  *
598  * Arguments:
599  *      howto           type of shutdown
600  *      eup             pointer to device unit structure
601  *
602  * Returns:
603  *      none
604  *
605  */
606 static void
607 eni_pci_shutdown ( eup, howto )
608         void    *eup;
609         int     howto;
610 {
611
612         /* Do device reset */
613         eni_pci_reset ( eup );
614
615 }