]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/pci/if_en_pci.c
Introduce support for Mandatory Access Control and extensible
[FreeBSD/FreeBSD.git] / sys / pci / if_en_pci.c
1 /*      $NetBSD: if_en_pci.c,v 1.1 1996/06/22 02:00:31 chuck Exp $      */
2
3 /*
4  *
5  * Copyright (c) 1996 Charles D. Cranor and Washington University.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by Charles D. Cranor and
19  *      Washington University.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * $FreeBSD$
35  */
36
37 /*
38  *
39  * i f _ e n _ p c i . c  
40  *
41  * author: Chuck Cranor <chuck@ccrc.wustl.edu>
42  * started: spring, 1996.
43  *
44  * FreeBSD PCI glue for the eni155p card.
45  * thanks to Matt Thomas for figuring out FreeBSD vs NetBSD vs etc.. diffs.
46  */
47
48 #include <sys/param.h>
49 #include <sys/kernel.h>
50 #include <sys/systm.h>
51 #include <sys/socket.h>
52
53 #include <sys/bus.h>
54 #include <machine/bus.h>
55 #include <sys/rman.h>
56 #include <machine/resource.h>
57
58 #include <net/if.h>
59
60 #include <pci/pcivar.h>
61 #include <pci/pcireg.h>
62
63 #include <dev/en/midwayreg.h>
64 #include <dev/en/midwayvar.h>
65
66 /*
67  * prototypes
68  */
69
70 static  int en_pci_probe(device_t);
71 static  int en_pci_attach(device_t);
72 static  int en_pci_detach(device_t);
73 static  int en_pci_shutdown(device_t);
74
75 /*
76  * local structures
77  */
78
79 struct en_pci_softc {
80   /* bus independent stuff */
81   struct en_softc esc;          /* includes "device" structure */
82
83   /* freebsd newbus glue */
84   struct resource *res;         /* resource descriptor for registers */
85   struct resource *irq;         /* resource descriptor for interrupt */
86   void *ih;                     /* interrupt handle */
87 };
88
89 #if !defined(MIDWAY_ENIONLY)
90 static  void eni_get_macaddr(device_t, struct en_pci_softc *);
91 #endif
92 #if !defined(MIDWAY_ADPONLY)
93 static  void adp_get_macaddr(struct en_pci_softc *);
94 #endif
95
96 /*
97  * local defines (PCI specific stuff)
98  */
99
100 /* 
101  * address of config base memory address register in PCI config space
102  * (this is card specific)
103  */
104         
105 #define PCI_CBMA        0x10
106
107 /*
108  * tonga (pci bridge).   ENI cards only!
109  */
110
111 #define EN_TONGA        0x60            /* PCI config addr of tonga reg */
112
113 #define TONGA_SWAP_DMA  0x80            /* endian swap control */
114 #define TONGA_SWAP_BYTE 0x40
115 #define TONGA_SWAP_WORD 0x20
116
117 /*
118  * adaptec pci bridge.   ADP cards only!
119  */
120
121 #define ADP_PCIREG      0x050040        /* PCI control register */
122
123 #define ADP_PCIREG_RESET        0x1     /* reset card */
124 #define ADP_PCIREG_IENABLE      0x2     /* interrupt enable */
125 #define ADP_PCIREG_SWAP_WORD    0x4     /* swap byte on slave access */
126 #define ADP_PCIREG_SWAP_DMA     0x8     /* swap byte on DMA */
127
128 #define PCI_VENDOR_EFFICIENTNETS 0x111a                 /* Efficent Networks */
129 #define PCI_PRODUCT_EFFICIENTNETS_ENI155PF 0x0000       /* ENI-155P ATM */
130 #define PCI_PRODUCT_EFFICIENTNETS_ENI155PA 0x0002       /* ENI-155P ATM */
131 #define PCI_VENDOR_ADP 0x9004                           /* adaptec */
132 #define PCI_PRODUCT_ADP_AIC5900 0x5900
133 #define PCI_PRODUCT_ADP_AIC5905 0x5905
134 #define PCI_VENDOR(x)           ((x) & 0xFFFF)
135 #define PCI_CHIPID(x)           (((x) >> 16) & 0xFFFF)
136
137 #if !defined(MIDWAY_ENIONLY)
138
139 static void adp_busreset(void *);
140
141 /*
142  * bus specific reset function [ADP only!]
143  */
144
145 static void adp_busreset(v)
146
147 void *v;
148
149 {
150   struct en_softc *sc = (struct en_softc *) v;
151   u_int32_t dummy;
152
153   bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG, ADP_PCIREG_RESET);
154   DELAY(1000);  /* let it reset */
155   dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
156   bus_space_write_4(sc->en_memt, sc->en_base, ADP_PCIREG, 
157                 (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA|ADP_PCIREG_IENABLE));
158   dummy = bus_space_read_4(sc->en_memt, sc->en_base, ADP_PCIREG);
159   if ((dummy & (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA)) !=
160                 (ADP_PCIREG_SWAP_WORD|ADP_PCIREG_SWAP_DMA))
161     printf("adp_busreset: Adaptec ATM did NOT reset!\n");
162 }
163 #endif
164
165 /***********************************************************************/
166
167 /*
168  * autoconfig stuff
169  */
170
171 static int
172 en_pci_probe(device_t dev)
173 {
174   switch (pci_get_vendor(dev)) {
175 #if !defined(MIDWAY_ADPONLY)
176   case PCI_VENDOR_EFFICIENTNETS:
177     switch (pci_get_device(dev)) {
178     case PCI_PRODUCT_EFFICIENTNETS_ENI155PF:
179     case PCI_PRODUCT_EFFICIENTNETS_ENI155PA:
180       device_set_desc(dev, "Efficient Networks ENI-155p");
181       return 0;
182     }
183     break;
184 #endif
185 #if !defined(MIDWAY_ENIONLY)
186   case PCI_VENDOR_ADP:
187     switch (pci_get_device(dev)) {
188     case PCI_PRODUCT_ADP_AIC5900:
189     case PCI_PRODUCT_ADP_AIC5905:
190       device_set_desc(dev, "Adaptec 155 ATM");
191       return 0;
192     }
193     break;
194 #endif
195   }
196   return ENXIO;
197 }
198
199 static int
200 en_pci_attach(device_t dev)
201 {
202   struct en_softc *sc;
203   struct en_pci_softc *scp;
204   u_long val;
205   int rid, s, unit, error = 0;
206
207   sc = device_get_softc(dev);
208   scp = (struct en_pci_softc *)sc;
209   bzero(scp, sizeof(*scp));             /* zero */
210
211   s = splimp();
212
213   /*
214    * Enable bus mastering.
215    */
216   val = pci_read_config(dev, PCIR_COMMAND, 2);
217   val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
218   pci_write_config(dev, PCIR_COMMAND, val, 2);
219
220   /*
221    * Map control/status registers.
222    */
223   rid = PCI_CBMA;
224   scp->res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
225                                 0, ~0, 1, RF_ACTIVE);
226   if (!scp->res) {
227     device_printf(dev, "could not map memory\n");
228     error = ENXIO;
229     goto fail;
230   }
231
232   sc->en_memt = rman_get_bustag(scp->res);
233   sc->en_base = rman_get_bushandle(scp->res);
234   
235   /*
236    * Allocate our interrupt.
237    */
238   rid = 0;
239   scp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
240                                 RF_SHAREABLE | RF_ACTIVE);
241   if (scp->irq == NULL) {
242     device_printf(dev, "could not map interrupt\n");
243     bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res);
244     error = ENXIO;
245     goto fail;
246   }
247
248   error = bus_setup_intr(dev, scp->irq, INTR_TYPE_NET,
249                          en_intr, sc, &scp->ih);
250   if (error) {
251     device_printf(dev, "could not setup irq\n");
252     bus_release_resource(dev, SYS_RES_IRQ, 0, scp->irq);
253     bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res);
254     goto fail;
255   }
256   sc->ipl = 1; /* XXX (required to enable interrupt on midway) */
257
258   unit = device_get_unit(dev);
259   snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname), "en%d", unit);
260   sc->enif.if_unit = unit;
261   sc->enif.if_name = "en";
262
263   /* figure out if we are an adaptec card or not */
264   sc->is_adaptec = (pci_get_vendor(dev) == PCI_VENDOR_ADP) ? 1 : 0;
265   
266   /*
267    * set up pci bridge
268    */
269 #if !defined(MIDWAY_ENIONLY)
270   if (sc->is_adaptec) {
271     adp_get_macaddr(scp);
272     sc->en_busreset = adp_busreset;
273     adp_busreset(sc);
274   }
275 #endif
276
277 #if !defined(MIDWAY_ADPONLY)
278   if (!sc->is_adaptec) {
279     eni_get_macaddr(dev, scp);
280     sc->en_busreset = NULL;
281     pci_write_config(dev, EN_TONGA, (TONGA_SWAP_DMA|TONGA_SWAP_WORD), 4);
282   }
283 #endif
284
285   /*
286    * done PCI specific stuff
287    */
288
289   en_attach(sc);
290
291   splx(s);
292
293   return 0;
294
295  fail:
296   splx(s);
297   return error;
298 }
299
300 static int
301 en_pci_detach(device_t dev)
302 {
303         struct en_softc *sc = device_get_softc(dev);
304         struct en_pci_softc *scp = (struct en_pci_softc *)sc;
305         int s;
306
307         s = splimp();
308
309         /*
310          * Close down routes etc.
311          */
312         if_detach(&sc->enif);
313
314         /*
315          * Stop DMA and drop transmit queue.
316          */
317         en_reset(sc);
318
319         /*
320          * Deallocate resources.
321          */
322         bus_teardown_intr(dev, scp->irq, scp->ih);
323         bus_release_resource(dev, SYS_RES_IRQ, 0, scp->irq);
324         bus_release_resource(dev, SYS_RES_MEMORY, PCI_CBMA, scp->res);
325
326 #ifdef notyet
327         /*
328          * Free all the driver internal resources
329          */
330 #endif
331
332         splx(s);
333
334         return 0;
335 }
336
337 static int
338 en_pci_shutdown(device_t dev)
339 {
340     struct en_pci_softc *psc = (struct en_pci_softc *)device_get_softc(dev);
341
342     en_reset(&psc->esc);
343     DELAY(10);  /* is this necessary? */
344     return (0);
345 }
346
347 #if !defined(MIDWAY_ENIONLY)
348
349 #if defined(sparc)
350 #define bus_space_read_1(t, h, o) \
351                 ((void)t, (*(volatile u_int8_t *)((h) + (o))))
352 #endif
353
354 static void 
355 adp_get_macaddr(scp)
356      struct en_pci_softc *scp;
357 {
358   struct en_softc * sc = (struct en_softc *)scp;
359   int lcv;
360
361   for (lcv = 0; lcv < sizeof(sc->macaddr); lcv++)
362           sc->macaddr[lcv] = bus_space_read_1(sc->en_memt, sc->en_base,
363                                               MID_ADPMACOFF + lcv);
364 }
365
366 #endif /* MIDWAY_ENIONLY */
367
368 #if !defined(MIDWAY_ADPONLY)
369
370 /*
371  * Read station (MAC) address from serial EEPROM.
372  * derived from linux drivers/atm/eni.c by Werner Almesberger, EPFL LRC.
373  */
374 #define EN_PROM_MAGIC  0x0c
375 #define EN_PROM_DATA   0x02
376 #define EN_PROM_CLK    0x01
377 #define EN_ESI         64
378
379 static void 
380 eni_get_macaddr(device_t dev, struct en_pci_softc *scp)
381 {
382   struct en_softc * sc = (struct en_softc *)scp;
383   int i, j, address, status;
384   u_int32_t data, t_data;
385   u_int8_t tmp;
386   
387   t_data = pci_read_config(dev, EN_TONGA, 4) & 0xffffff00;
388
389   data =  EN_PROM_MAGIC | EN_PROM_DATA | EN_PROM_CLK;
390   pci_write_config(dev, EN_TONGA, data, 4);
391
392   for (i = 0; i < sizeof(sc->macaddr); i ++){
393     /* start operation */
394     data |= EN_PROM_DATA ;
395     pci_write_config(dev, EN_TONGA, data, 4);
396     data |= EN_PROM_CLK ;
397     pci_write_config(dev, EN_TONGA, data, 4);
398     data &= ~EN_PROM_DATA ;
399     pci_write_config(dev, EN_TONGA, data, 4);
400     data &= ~EN_PROM_CLK ;
401     pci_write_config(dev, EN_TONGA, data, 4);
402     /* send address with serial line */
403     address = ((i + EN_ESI) << 1) + 1;
404     for ( j = 7 ; j >= 0 ; j --){
405       data = (address >> j) & 1 ? data | EN_PROM_DATA :
406       data & ~EN_PROM_DATA;
407       pci_write_config(dev, EN_TONGA, data, 4);
408       data |= EN_PROM_CLK ;
409       pci_write_config(dev, EN_TONGA, data, 4);
410       data &= ~EN_PROM_CLK ;
411       pci_write_config(dev, EN_TONGA, data, 4);
412     }
413     /* get ack */
414     data |= EN_PROM_DATA ;
415     pci_write_config(dev, EN_TONGA, data, 4);
416     data |= EN_PROM_CLK ;
417     pci_write_config(dev, EN_TONGA, data, 4);
418     data = pci_read_config(dev, EN_TONGA, 4);
419     status = data & EN_PROM_DATA;
420     data &= ~EN_PROM_CLK ;
421     pci_write_config(dev, EN_TONGA, data, 4);
422     data |= EN_PROM_DATA ;
423     pci_write_config(dev, EN_TONGA, data, 4);
424
425     tmp = 0;
426
427     for ( j = 7 ; j >= 0 ; j --){
428       tmp <<= 1;
429       data |= EN_PROM_DATA ;
430       pci_write_config(dev, EN_TONGA, data, 4);
431       data |= EN_PROM_CLK ;
432       pci_write_config(dev, EN_TONGA, data, 4);
433       data = pci_read_config(dev, EN_TONGA, 4);
434       if(data & EN_PROM_DATA) tmp |= 1;
435       data &= ~EN_PROM_CLK ;
436       pci_write_config(dev, EN_TONGA, data, 4);
437       data |= EN_PROM_DATA ;
438       pci_write_config(dev, EN_TONGA, data, 4);
439     }
440     /* get ack */
441     data |= EN_PROM_DATA ;
442     pci_write_config(dev, EN_TONGA, data, 4);
443     data |= EN_PROM_CLK ;
444     pci_write_config(dev, EN_TONGA, data, 4);
445     data = pci_read_config(dev, EN_TONGA, 4);
446     status = data & EN_PROM_DATA;
447     data &= ~EN_PROM_CLK ;
448     pci_write_config(dev, EN_TONGA, data, 4);
449     data |= EN_PROM_DATA ;
450     pci_write_config(dev, EN_TONGA, data, 4);
451
452     sc->macaddr[i] = tmp;
453   }
454   /* stop operation */
455   data &=  ~EN_PROM_DATA;
456   pci_write_config(dev, EN_TONGA, data, 4);
457   data |=  EN_PROM_CLK;
458   pci_write_config(dev, EN_TONGA, data, 4);
459   data |=  EN_PROM_DATA;
460   pci_write_config(dev, EN_TONGA, data, 4);
461   pci_write_config(dev, EN_TONGA, t_data, 4);
462 }
463
464 #endif /* !MIDWAY_ADPONLY */
465
466 static device_method_t en_methods[] = {
467         /* Device interface */
468         DEVMETHOD(device_probe,         en_pci_probe),
469         DEVMETHOD(device_attach,        en_pci_attach),
470         DEVMETHOD(device_detach,        en_pci_detach),
471         DEVMETHOD(device_shutdown,      en_pci_shutdown),
472
473         { 0, 0 }
474 };
475
476 static driver_t en_driver = {
477         "en",
478         en_methods,
479         sizeof(struct en_pci_softc),
480 };
481
482 static devclass_t en_devclass;
483
484 DRIVER_MODULE(if_en, pci, en_driver, en_devclass, 0, 0);
485