FreeBSD/Linux Kernel Cross Reference
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.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
|