76a53c3aa9d57b86c0f4964bfb366aab269f5008
[dragonfly.git] / sys / dev / netif / lnc / if_lnc_isa.c
1 /*      $NetBSD: if_le_isa.c,v 1.41 2005/12/24 20:27:41 perry Exp $     */
2 /*      $FreeBSD: src/sys/dev/le/if_le_isa.c,v 1.1 2006/05/17 21:25:22 marius Exp $     */
3 /*      $DragonFly: src/sys/dev/netif/lnc/if_lnc_isa.c,v 1.12 2008/08/17 04:32:33 sephe Exp $   */
4
5 /*-
6  * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by Charles M. Hannum and by Jason R. Thorpe of the Numerical Aerospace
11  * Simulation Facility, NASA Ames Research Center.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *      This product includes software developed by the NetBSD
24  *      Foundation, Inc. and its contributors.
25  * 4. Neither the name of The NetBSD Foundation nor the names of its
26  *    contributors may be used to endorse or promote products derived
27  *    from this software without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39  * POSSIBILITY OF SUCH DAMAGE.
40  */
41
42 /*-
43  * Copyright (c) 1992, 1993
44  *      The Regents of the University of California.  All rights reserved.
45  *
46  * This code is derived from software contributed to Berkeley by
47  * Ralph Campbell and Rick Macklem.
48  *
49  * Redistribution and use in source and binary forms, with or without
50  * modification, are permitted provided that the following conditions
51  * are met:
52  * 1. Redistributions of source code must retain the above copyright
53  *    notice, this list of conditions and the following disclaimer.
54  * 2. Redistributions in binary form must reproduce the above copyright
55  *    notice, this list of conditions and the following disclaimer in the
56  *    documentation and/or other materials provided with the distribution.
57  * 3. Neither the name of the University nor the names of its contributors
58  *    may be used to endorse or promote products derived from this software
59  *    without specific prior written permission.
60  *
61  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
62  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
63  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
65  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
66  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
67  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
68  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
69  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
70  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
71  * SUCH DAMAGE.
72  *
73  *      @(#)if_le.c     8.2 (Berkeley) 11/16/93
74  */
75
76 #include <sys/param.h>
77 #include <sys/systm.h>
78 #include <sys/bus.h>
79 #include <sys/endian.h>
80 #include <sys/kernel.h>
81 #include <sys/interrupt.h>
82 #include <sys/lock.h>
83 #include <sys/module.h>
84 #include <sys/resource.h>
85 #include <sys/rman.h>
86 #include <sys/socket.h>
87
88 #include <net/ethernet.h>
89 #include <net/if.h>
90 #include <net/if_media.h>
91 #include <net/if_arp.h>
92
93 #include <bus/isa/isavar.h>
94
95 #include <dev/netif/lnc/lancereg.h>
96 #include <dev/netif/lnc/lancevar.h>
97 #include <dev/netif/lnc/am7990var.h>
98
99 #define LE_ISA_MEMSIZE  (16*1024)
100 #define PCNET_RDP       0x10
101 #define PCNET_RAP       0x12
102
103 struct le_isa_softc {
104         struct am7990_softc     sc_am7990;      /* glue to MI code */
105
106         bus_size_t              sc_rap;         /* offsets to LANCE... */
107         bus_size_t              sc_rdp;         /* ...registers */
108
109         int                     sc_rrid;
110         struct resource         *sc_rres;
111         bus_space_tag_t         sc_regt;
112         bus_space_handle_t      sc_regh;
113
114         int                     sc_drid;
115         struct resource         *sc_dres;
116
117         int                     sc_irid;
118         struct resource         *sc_ires;
119         void                    *sc_ih;
120
121         bus_dma_tag_t           sc_pdmat;
122         bus_dma_tag_t           sc_dmat;
123         bus_dmamap_t            sc_dmam;
124 };
125
126 static device_probe_t le_isa_probe;
127 static device_attach_t le_isa_attach;
128 static device_detach_t le_isa_detach;
129 static device_resume_t le_isa_resume;
130 static device_suspend_t le_isa_suspend;
131
132 static device_method_t le_isa_methods[] = {
133         /* Device interface */
134         DEVMETHOD(device_probe,         le_isa_probe),
135         DEVMETHOD(device_attach,        le_isa_attach),
136         DEVMETHOD(device_detach,        le_isa_detach),
137         /* We can just use the suspend method here. */
138         DEVMETHOD(device_shutdown,      le_isa_suspend),
139         DEVMETHOD(device_suspend,       le_isa_suspend),
140         DEVMETHOD(device_resume,        le_isa_resume),
141
142         { 0, 0 }
143 };
144
145 DEFINE_CLASS_0(lnc, le_isa_driver, le_isa_methods, sizeof(struct le_isa_softc));
146 DRIVER_MODULE(lnc, isa, le_isa_driver, le_devclass, 0, 0);
147 MODULE_DEPEND(lnc, ether, 1, 1, 1);
148
149 struct le_isa_param {
150         const char      *name;
151         u_long          iosize;
152         bus_size_t      rap;
153         bus_size_t      rdp;
154         bus_size_t      macstart;
155         int             macstride;
156 } static const le_isa_params[] = {
157         { "BICC Isolan", 24, 0xe, 0xc, 0, 2 },
158         { "Novell NE2100", 16, 0x12, 0x10, 0, 1 }
159 };
160
161 static struct isa_pnp_id le_isa_ids[] = {
162         { 0x0322690e, "Cabletron E2200 Single Chip" },  /* CSI2203 */
163         { 0x0110490a, "Boca LANCard Combo" },           /* BRI1001 */
164         { 0x0100a60a, "Melco Inc. LGY-IV" },            /* BUF0001 */
165         { 0xd880d041, "Novell NE2100" },                /* PNP80D8 */
166         { 0x0082d041, "Cabletron E2100 Series DNI" },   /* PNP8200 */
167         { 0x3182d041, "AMD AM1500T/AM2100" },           /* PNP8231 */
168         { 0x8c82d041, "AMD PCnet-ISA" },                /* PNP828C */
169         { 0x8d82d041, "AMD PCnet-32" },                 /* PNP828D */
170         { 0xcefaedfe, "Racal InterLan EtherBlaster" },  /* _WMFACE */
171         { 0, NULL }
172 };
173
174 static void le_isa_wrcsr(struct lance_softc *, uint16_t, uint16_t);
175 static uint16_t le_isa_rdcsr(struct lance_softc *, uint16_t);
176 static bus_dmamap_callback_t le_isa_dma_callback;
177 static int le_isa_probe_legacy(device_t, const struct le_isa_param *);
178
179 static void
180 le_isa_wrcsr(struct lance_softc *sc, uint16_t port, uint16_t val)
181 {
182         struct le_isa_softc *lesc = (struct le_isa_softc *)sc;
183
184         bus_space_write_2(lesc->sc_regt, lesc->sc_regh, lesc->sc_rap, port);
185         bus_space_barrier(lesc->sc_regt, lesc->sc_regh, lesc->sc_rap, 2,
186             BUS_SPACE_BARRIER_WRITE);
187         bus_space_write_2(lesc->sc_regt, lesc->sc_regh, lesc->sc_rdp, val);
188 }
189
190 static uint16_t
191 le_isa_rdcsr(struct lance_softc *sc, uint16_t port)
192 {
193         struct le_isa_softc *lesc = (struct le_isa_softc *)sc;
194
195         bus_space_write_2(lesc->sc_regt, lesc->sc_regh, lesc->sc_rap, port);
196         bus_space_barrier(lesc->sc_regt, lesc->sc_regh, lesc->sc_rap, 2,
197             BUS_SPACE_BARRIER_WRITE);
198         return (bus_space_read_2(lesc->sc_regt, lesc->sc_regh, lesc->sc_rdp));
199 }
200
201 static void
202 le_isa_dma_callback(void *xsc, bus_dma_segment_t *segs, int nsegs, int error)
203 {
204         struct lance_softc *sc = (struct lance_softc *)xsc;
205
206         if (error != 0)
207                 return;
208         KASSERT(nsegs == 1, ("%s: bad DMA segment count", __func__));
209         sc->sc_addr = segs[0].ds_addr;
210 }
211
212 static int
213 le_isa_probe_legacy(device_t dev, const struct le_isa_param *leip)
214 {
215         struct le_isa_softc *lesc;
216         struct lance_softc *sc;
217         int error;
218
219         lesc = device_get_softc(dev);
220         sc = &lesc->sc_am7990.lsc;
221
222         lesc->sc_rrid = 0;
223         lesc->sc_rres = bus_alloc_resource(dev, SYS_RES_IOPORT, &lesc->sc_rrid,
224             0, ~0, leip->iosize, RF_ACTIVE);
225         if (lesc->sc_rres == NULL)
226                 return (ENXIO);
227         lesc->sc_regt = rman_get_bustag(lesc->sc_rres);
228         lesc->sc_regh = rman_get_bushandle(lesc->sc_rres);
229         lesc->sc_rap = leip->rap;
230         lesc->sc_rdp = leip->rdp;
231
232         /* Stop the chip and put it in a known state. */
233         le_isa_wrcsr(sc, LE_CSR0, LE_C0_STOP);
234         DELAY(100);
235         if (le_isa_rdcsr(sc, LE_CSR0) != LE_C0_STOP) {
236                 error = ENXIO;
237                 goto fail;
238         }
239         le_isa_wrcsr(sc, LE_CSR3, 0);
240         error = 0;
241
242  fail:
243         bus_release_resource(dev, SYS_RES_IOPORT, lesc->sc_rrid, lesc->sc_rres);
244         return (error);
245 }
246
247 static int
248 le_isa_probe(device_t dev)
249 {
250         int i;
251
252         switch (ISA_PNP_PROBE(device_get_parent(dev), dev, le_isa_ids)) {
253         case 0:
254                 return (-20);
255         case ENOENT:
256                 for (i = 0; i < sizeof(le_isa_params) /
257                     sizeof(le_isa_params[0]); i++) {
258                         if (le_isa_probe_legacy(dev, &le_isa_params[i]) == 0) {
259                                 device_set_desc(dev, le_isa_params[i].name);
260                                 return (-20);
261                         }
262                 }
263                 /* FALLTHROUGH */
264         case ENXIO:
265         default:
266                 return (ENXIO);
267         }
268 }
269
270 static int
271 le_isa_attach(device_t dev)
272 {
273         struct le_isa_softc *lesc;
274         struct lance_softc *sc;
275         bus_size_t macstart, rap, rdp;
276         int error, i, macstride;
277
278         lesc = device_get_softc(dev);
279         sc = &lesc->sc_am7990.lsc;
280
281         lesc->sc_rrid = 0;
282         switch (ISA_PNP_PROBE(device_get_parent(dev), dev, le_isa_ids)) {
283         case 0:
284                 lesc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
285                     &lesc->sc_rrid, RF_ACTIVE);
286                 rap = PCNET_RAP;
287                 rdp = PCNET_RDP;
288                 macstart = 0;
289                 macstride = 1;
290                 break;
291         case ENOENT:
292                 for (i = 0; i < sizeof(le_isa_params) /
293                     sizeof(le_isa_params[0]); i++) {
294                         if (le_isa_probe_legacy(dev, &le_isa_params[i]) == 0) {
295                                 lesc->sc_rres = bus_alloc_resource(dev,
296                                     SYS_RES_IOPORT, &lesc->sc_rrid, 0, ~0,
297                                     le_isa_params[i].iosize, RF_ACTIVE);
298                                 rap = le_isa_params[i].rap;
299                                 rdp = le_isa_params[i].rdp;
300                                 macstart = le_isa_params[i].macstart;
301                                 macstride = le_isa_params[i].macstride;
302                                 goto found;
303                         }
304                 }
305                 /* FALLTHROUGH */
306         case ENXIO:
307         default:
308                 device_printf(dev, "cannot determine chip\n");
309                 error = ENXIO;
310                 goto fail_mtx;
311         }
312
313  found:
314         if (lesc->sc_rres == NULL) {
315                 device_printf(dev, "cannot allocate registers\n");
316                 error = ENXIO;
317                 goto fail_mtx;
318         }
319         lesc->sc_regt = rman_get_bustag(lesc->sc_rres);
320         lesc->sc_regh = rman_get_bushandle(lesc->sc_rres);
321         lesc->sc_rap = rap;
322         lesc->sc_rdp = rdp;
323
324         lesc->sc_drid = 0;
325         if ((lesc->sc_dres = bus_alloc_resource_any(dev, SYS_RES_DRQ,
326             &lesc->sc_drid, RF_ACTIVE)) == NULL) {
327                 device_printf(dev, "cannot allocate DMA channel\n");
328                 error = ENXIO;
329                 goto fail_rres;
330         }
331
332         lesc->sc_irid = 0;
333         if ((lesc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ,
334             &lesc->sc_irid, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
335                 device_printf(dev, "cannot allocate interrupt\n");
336                 error = ENXIO;
337                 goto fail_dres;
338         }
339
340         error = bus_dma_tag_create(
341             NULL,                       /* parent */
342             1, 0,                       /* alignment, boundary */
343             BUS_SPACE_MAXADDR_24BIT,    /* lowaddr */
344             BUS_SPACE_MAXADDR,          /* highaddr */
345             NULL, NULL,                 /* filter, filterarg */
346             BUS_SPACE_MAXSIZE_32BIT,    /* maxsize */
347             0,                          /* nsegments */
348             BUS_SPACE_MAXSIZE_32BIT,    /* maxsegsize */
349             BUS_DMA_WAITOK,             /* flags */
350             &lesc->sc_pdmat);
351         if (error != 0) {
352                 device_printf(dev, "cannot allocate parent DMA tag\n");
353                 goto fail_ires;
354         }
355
356         sc->sc_memsize = LE_ISA_MEMSIZE;
357         /*
358          * For Am79C90, Am79C961 and Am79C961A the init block must be 2-byte
359          * aligned and the ring descriptors must be 8-byte aligned.
360          */
361         error = bus_dma_tag_create(
362             lesc->sc_pdmat,             /* parent */
363             8, 0,                       /* alignment, boundary */
364             BUS_SPACE_MAXADDR_24BIT,    /* lowaddr */
365             BUS_SPACE_MAXADDR,          /* highaddr */
366             NULL, NULL,                 /* filter, filterarg */
367             sc->sc_memsize,             /* maxsize */
368             1,                          /* nsegments */
369             sc->sc_memsize,             /* maxsegsize */
370             BUS_DMA_WAITOK,             /* flags */
371             &lesc->sc_dmat);
372         if (error != 0) {
373                 device_printf(dev, "cannot allocate buffer DMA tag\n");
374                 goto fail_pdtag;
375         }
376
377         error = bus_dmamem_alloc(lesc->sc_dmat, (void **)&sc->sc_mem,
378             BUS_DMA_WAITOK | BUS_DMA_COHERENT, &lesc->sc_dmam);
379         if (error != 0) {
380                 device_printf(dev, "cannot allocate DMA buffer memory\n");
381                 goto fail_dtag;
382         }
383
384         sc->sc_addr = 0;
385         error = bus_dmamap_load(lesc->sc_dmat, lesc->sc_dmam, sc->sc_mem,
386             sc->sc_memsize, le_isa_dma_callback, sc, 0);
387         if (error != 0 || sc->sc_addr == 0) {
388                 device_printf(dev, "cannot load DMA buffer map\n");
389                 goto fail_dmem;
390         }
391
392         isa_dmacascade(rman_get_start(lesc->sc_dres));
393
394         sc->sc_flags = 0;
395         sc->sc_conf3 = 0;
396
397         /*
398          * Extract the physical MAC address from the ROM.
399          */
400         for (i = 0; i < sizeof(sc->sc_enaddr); i++)
401                 sc->sc_enaddr[i] =  bus_space_read_1(lesc->sc_regt,
402                     lesc->sc_regh, macstart + i * macstride);
403
404         sc->sc_copytodesc = lance_copytobuf_contig;
405         sc->sc_copyfromdesc = lance_copyfrombuf_contig;
406         sc->sc_copytobuf = lance_copytobuf_contig;
407         sc->sc_copyfrombuf = lance_copyfrombuf_contig;
408         sc->sc_zerobuf = lance_zerobuf_contig;
409
410         sc->sc_rdcsr = le_isa_rdcsr;
411         sc->sc_wrcsr = le_isa_wrcsr;
412         sc->sc_hwreset = NULL;
413         sc->sc_hwinit = NULL;
414         sc->sc_hwintr = NULL;
415         sc->sc_nocarrier = NULL;
416         sc->sc_mediachange = NULL;
417         sc->sc_mediastatus = NULL;
418         sc->sc_supmedia = NULL;
419
420         error = am7990_config(&lesc->sc_am7990, device_get_name(dev),
421             device_get_unit(dev));
422         if (error != 0) {
423                 device_printf(dev, "cannot attach Am7990\n");
424                 goto fail_dmap;
425         }
426
427         error = bus_setup_intr(dev, lesc->sc_ires, INTR_MPSAFE,
428             am7990_intr, sc, &lesc->sc_ih, sc->ifp->if_serializer);
429         if (error != 0) {
430                 device_printf(dev, "cannot set up interrupt\n");
431                 goto fail_am7990;
432         }
433
434         sc->ifp->if_cpuid = ithread_cpuid(rman_get_start(lesc->sc_ires));
435         KKASSERT(sc->ifp->if_cpuid >= 0 && sc->ifp->if_cpuid < ncpus);
436
437         return (0);
438
439  fail_am7990:
440         am7990_detach(&lesc->sc_am7990);
441  fail_dmap:
442         bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam);
443  fail_dmem:
444         bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam);
445  fail_dtag:
446         bus_dma_tag_destroy(lesc->sc_dmat);
447  fail_pdtag:
448         bus_dma_tag_destroy(lesc->sc_pdmat);
449  fail_ires:
450         bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires);
451  fail_dres:
452         bus_release_resource(dev, SYS_RES_DRQ, lesc->sc_drid, lesc->sc_dres);
453  fail_rres:
454         bus_release_resource(dev, SYS_RES_IOPORT, lesc->sc_rrid, lesc->sc_rres);
455  fail_mtx:
456         return (error);
457 }
458
459 static int
460 le_isa_detach(device_t dev)
461 {
462         struct le_isa_softc *lesc;
463         struct lance_softc *sc;
464
465         lesc = device_get_softc(dev);
466         sc = &lesc->sc_am7990.lsc;
467
468         if (device_is_attached(dev)) {
469                 lwkt_serialize_enter(sc->ifp->if_serializer);
470                 lance_stop(sc);
471                 bus_teardown_intr(dev, lesc->sc_ires, lesc->sc_ih);
472                 lwkt_serialize_exit(sc->ifp->if_serializer);
473
474                 am7990_detach(&lesc->sc_am7990);
475         }
476
477         if (lesc->sc_ires)
478                 bus_release_resource(dev, SYS_RES_IRQ, lesc->sc_irid, lesc->sc_ires);
479         if (lesc->sc_dres)
480                 bus_release_resource(dev, SYS_RES_DRQ, lesc->sc_drid, lesc->sc_dres);
481         if (lesc->sc_rres)
482                 bus_release_resource(dev, SYS_RES_IOPORT, lesc->sc_rrid, lesc->sc_rres);
483         if (lesc->sc_dmam) {
484                 bus_dmamap_unload(lesc->sc_dmat, lesc->sc_dmam);
485                 bus_dmamem_free(lesc->sc_dmat, sc->sc_mem, lesc->sc_dmam);
486         }
487         if (lesc->sc_dmat)
488                 bus_dma_tag_destroy(lesc->sc_dmat);
489         if (lesc->sc_pdmat)
490                 bus_dma_tag_destroy(lesc->sc_pdmat);
491
492         return (0);
493 }
494
495 static int
496 le_isa_suspend(device_t dev)
497 {
498         struct le_isa_softc *lesc;
499
500         lesc = device_get_softc(dev);
501
502         lance_suspend(&lesc->sc_am7990.lsc);
503
504         return (0);
505 }
506
507 static int
508 le_isa_resume(device_t dev)
509 {
510         struct le_isa_softc *lesc;
511
512         lesc = device_get_softc(dev);
513
514         lance_resume(&lesc->sc_am7990.lsc);
515
516         return (0);
517 }