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