60db66b660025952defe4785aa4690769b87cb32
[dragonfly.git] / sys / dev / serial / digi / digi_isa.c
1 /*-
2  * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
3  *   based on work by Slawa Olhovchenkov
4  *                    John Prince <johnp@knight-trosoft.com>
5  *                    Eric Hernes
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  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD: src/sys/dev/digi/digi_isa.c,v 1.12 2003/08/24 17:46:03 obrien Exp $
30  */
31
32 /*-
33  * TODO:
34  *      Figure out how to make the non-Xi boards use memory addresses other
35  *      than 0xd0000 !!!
36  */
37
38 #include <sys/param.h>
39
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/tty.h>
43 #include <sys/bus.h>
44 #include <sys/rman.h>
45
46 #include <vm/vm.h>
47 #include <vm/pmap.h>
48
49 #include <dev/serial/digi/digiio.h>
50 #include <dev/serial/digi/digireg.h>
51 #include <dev/serial/digi/digi.h>
52
53 /* Valid i/o addresses are any of these with either 0 or 4 added */
54 static u_long digi_validio[] = {
55         0x100, 0x110, 0x120, 0x200, 0x220, 0x300, 0x320
56 };
57 #define DIGI_NVALIDIO   NELEM(digi_validio)
58 #define IO_SIZE         0x04
59
60 static u_long digi_validmem[] = {
61         0x80000, 0x88000, 0x90000, 0x98000, 0xa0000, 0xa8000, 0xb0000, 0xb8000,
62         0xc0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000, 0xe8000, 0xf0000, 0xf8000,
63         0xf0000000, 0xf1000000, 0xf2000000, 0xf3000000, 0xf4000000, 0xf5000000,
64         0xf6000000, 0xf7000000, 0xf8000000, 0xf9000000, 0xfa000000, 0xfb000000,
65         0xfc000000, 0xfd000000, 0xfe000000, 0xff000000
66 };
67 #define DIGI_NVALIDMEM  NELEM(digi_validmem)
68
69 static u_char *
70 digi_isa_setwin(struct digi_softc *sc, unsigned int addr)
71 {
72         outb(sc->wport, sc->window = FEPWIN | (addr >> sc->win_bits));
73         return (sc->vmem + (addr % sc->win_size));
74 }
75
76 static u_char *
77 digi_xi_setwin(struct digi_softc *sc, unsigned int addr)
78 {
79         outb(sc->wport, sc->window = FEPMEM);
80         return (sc->vmem + addr);
81 }
82
83 static void
84 digi_isa_hidewin(struct digi_softc *sc)
85 {
86         outb(sc->wport, sc->window = 0);
87         /* outb(sc->port, 0); */
88 }
89
90 static void
91 digi_isa_towin(struct digi_softc *sc, int win)
92 {
93         outb(sc->wport, sc->window = win);
94 }
95
96 static void
97 digi_xi_towin(struct digi_softc *sc, int win)
98 {
99         outb(sc->wport, sc->window = FEPMEM);
100 }
101
102 /*
103  * sc->port should be set and its resource allocated.
104  */
105 static int
106 digi_isa_check(struct digi_softc *sc)
107 {
108         int i, ident;
109
110         sc->name = NULL;
111
112         /* Invasive probe - reset the card */
113         outb(sc->port, FEPRST);
114         for (i = 0; (inb(sc->port) & FEPMASK) != FEPRST; i++) {
115                 if (i == hz / 10)
116                         return (0);
117                 digi_delay(sc, "digirst", 1);
118         }
119         DLOG(DIGIDB_INIT, (sc->dev, "got reset after %d iterations\n", i));
120
121         ident = inb(sc->port);
122
123         /*
124          * NOTE, this probe is all wrong.  I haven't got the data sheets !
125          */
126
127         DLOG(DIGIDB_INIT, (sc->dev, "board type is 0x%x\n", ident));
128         if (ident & 0x1) {
129                 switch (ident) {
130                 case 0x05:
131                 case 0x15:
132                 case 0x25:
133                 case 0x35:
134                         sc->model = PCXI;
135                         sc->csigs = &digi_xixe_signals;
136                         switch (ident & 0x30) {
137                         case 0:
138                                 sc->name = "Digiboard PC/Xi 64K";
139                                 sc->mem_seg = 0xf000;
140                                 sc->win_size = 0x10000;
141                                 sc->win_bits = 16;
142                                 break;
143                         case 0x10:
144                                 sc->name = "Digiboard PC/Xi 128K";
145                                 sc->mem_seg = 0xE000;
146                                 sc->win_size = 0x20000;
147                                 sc->win_bits = 17;
148                                 break;
149                         case 0x20:
150                                 sc->name = "Digiboard PC/Xi 256K";
151                                 sc->mem_seg = 0xC000;
152                                 sc->win_size = 0x40000;
153                                 sc->win_bits = 18;
154                                 break;
155                         case 0x30:
156                                 sc->name = "Digiboard PC/Xi 512K";
157                                 sc->mem_seg = 0x8000;
158                                 sc->win_size = 0x80000;
159                                 sc->win_bits = 19;
160                                 break;
161                         }
162                         sc->wport = sc->port;
163                         sc->module = "Xe";
164
165                         sc->setwin = digi_xi_setwin;
166                         sc->hidewin = digi_isa_hidewin;
167                         sc->towin = digi_xi_towin;
168                         break;
169
170                 case 0xf5:
171                         sc->name = "Digiboard PC/Xem";
172                         sc->model = PCXEM;
173                         sc->csigs = &digi_normal_signals;
174                         sc->win_size = 0x8000;
175                         sc->win_bits = 15;
176                         sc->wport = sc->port + 1;
177                         sc->module = "Xem";
178
179                         sc->setwin = digi_isa_setwin;
180                         sc->hidewin = digi_isa_hidewin;
181                         sc->towin = digi_isa_towin;
182                         break;
183                 }
184         } else {
185                 outb(sc->port, 1);
186                 ident = inb(sc->port);
187
188                 if (ident & 0x1) {
189                         device_printf(sc->dev, "PC/Xm is unsupported\n");
190                         return (0);
191                 }
192
193                 sc->mem_seg = 0xf000;
194
195                 if (!(ident & 0xc0)) {
196                         sc->name = "Digiboard PC/Xe 64K";
197                         sc->model = PCXE;
198                         sc->csigs = &digi_xixe_signals;
199                         sc->win_size = 0x10000;
200                         sc->win_bits = 16;
201                         sc->wport = sc->port;
202                 } else {
203                         sc->name = "Digiboard PC/Xe 64/8K (windowed)";
204                         sc->model = PCXEVE;
205                         sc->csigs = &digi_normal_signals;
206                         sc->win_size = 0x2000;
207                         sc->win_bits = 13;
208                         sc->wport = sc->port + 1;
209                 }
210                 sc->module = "Xe";
211
212                 sc->setwin = digi_isa_setwin;
213                 sc->hidewin = digi_isa_hidewin;
214                 sc->towin = digi_isa_towin;
215         }
216
217         return (sc->name != NULL);
218 }
219
220 static int
221 digi_isa_probe(device_t dev)
222 {
223         struct digi_softc *sc = device_get_softc(dev);
224         int i;
225
226         KASSERT(sc, ("digi%d: softc not allocated in digi_isa_probe\n",
227             device_get_unit(dev)));
228
229         bzero(sc, sizeof(*sc));
230         sc->status = DIGI_STATUS_NOTINIT;
231         sc->dev = dev;
232         sc->res.unit = device_get_unit(dev);
233         if (sc->res.unit >= 16) {
234                 /* Don't overflow our control mask */
235                 device_printf(dev, "At most 16 digiboards may be used\n");
236                 return (ENXIO);
237         }
238         DLOG(DIGIDB_INIT, (sc->dev, "probing on isa bus\n"));
239
240         /* Check that we've got a valid i/o address */
241         if ((sc->port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0) {
242                 DLOG(DIGIDB_INIT, (sc->dev, "io address not given\n"));
243                 return (ENXIO);
244         }
245         for (i = 0; i < DIGI_NVALIDIO; i++)
246                 if (sc->port == digi_validio[i] ||
247                     sc->port == digi_validio[i] + 4)
248                         break;
249         if (i == DIGI_NVALIDIO) {
250                 device_printf(dev, "0x%03x: Invalid i/o address\n", sc->port);
251                 return (ENXIO);
252         }
253
254         /* Ditto for our memory address */
255         if ((sc->pmem = bus_get_resource_start(dev, SYS_RES_MEMORY, 0)) == 0)
256                 return (ENXIO);
257         for (i = 0; i < DIGI_NVALIDMEM; i++)
258                 if (sc->pmem == digi_validmem[i])
259                         break;
260         if (i == DIGI_NVALIDMEM) {
261                 device_printf(dev, "0x%lx: Invalid memory address\n", sc->pmem);
262                 return (ENXIO);
263         }
264         if ((sc->pmem & 0xfffffful) != sc->pmem) {
265                 device_printf(dev, "0x%lx: Memory address not supported\n",
266                     sc->pmem);
267                 return (ENXIO);
268         }
269         sc->vmem = (u_char *)sc->pmem;
270
271         DLOG(DIGIDB_INIT, (sc->dev, "isa? port 0x%03x mem 0x%lx\n",
272             sc->port, sc->pmem));
273
274         /* Temporarily map our io ports */
275         sc->res.iorid = 0;
276         sc->res.io = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->res.iorid,
277             0ul, ~0ul, IO_SIZE, RF_ACTIVE);
278         if (sc->res.io == NULL)
279                 return (ENXIO);
280
281         /* Check the type of card and get internal memory characteristics */
282         if (!digi_isa_check(sc)) {
283                 bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
284                     sc->res.io);
285                 return (ENXIO);
286         }
287
288         /* Temporarily map our memory */
289         sc->res.mrid = 0;
290         sc->res.mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->res.mrid,
291             0ul, ~0ul, sc->win_size, RF_ALLOCATED);
292         if (sc->res.mem == NULL) {
293                 device_printf(dev, "0x%lx: Memory range is in use\n", sc->pmem);
294                 bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
295                     sc->res.io);
296                 return (ENXIO);
297         }
298
299         outb(sc->port, FEPCLR);         /* drop RESET */
300         sc->hidewin(sc);        /* set initial sc->window */
301
302         bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid, sc->res.mem);
303         bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid, sc->res.io);
304
305         /* Let digi_isa_attach() know what we've found */
306         bus_set_resource(dev, SYS_RES_IOPORT, 0, sc->port, IO_SIZE);
307         bus_set_resource(dev, SYS_RES_MEMORY, 0, sc->pmem, sc->win_size);
308
309         DLOG(DIGIDB_INIT, (sc->dev, "Probe returns -10\n"));
310
311         return (-10);           /* Other drivers are preferred for now */
312 }
313
314 static int
315 digi_isa_attach(device_t dev)
316 {
317         struct digi_softc *sc = device_get_softc(dev);
318         int i, t, res;
319         u_char *ptr;
320         int reset;
321         u_long msize, iosize;
322         long scport;
323
324         KASSERT(sc, ("digi%d: softc not allocated in digi_isa_attach\n",
325             device_get_unit(dev)));
326
327         res = ENXIO;
328         bzero(sc, sizeof(*sc));
329         sc->status = DIGI_STATUS_NOTINIT;
330         sc->dev = dev;
331         sc->res.unit = device_get_unit(dev);
332         DLOG(DIGIDB_INIT, (sc->dev, "attaching\n"));
333
334         bus_get_resource(dev, SYS_RES_IOPORT, 0, &scport, &iosize);
335         bus_get_resource(dev, SYS_RES_MEMORY, 0, &sc->pmem, &msize);
336         sc->port = scport;
337         /* sc->altpin = !!(device_get_flags(dev) & DGBFLAG_ALTPIN); */
338
339         /* Allocate resources (verified in digi_isa_probe()) */
340         sc->res.iorid = 0;
341         sc->res.io = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->res.iorid,
342             0ul, ~0ul, iosize, RF_ACTIVE);
343         if (sc->res.io == NULL)
344                 return (ENXIO);
345
346         /* Check the type of card and get internal memory characteristics */
347         DLOG(DIGIDB_INIT, (sc->dev, "Checking card type\n"));
348         if (!digi_isa_check(sc))
349                 goto failed;
350
351         callout_init(&sc->callout);
352         callout_init(&sc->inttest);
353
354         sc->res.mrid = 0;
355         sc->res.mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->res.mrid,
356             0ul, ~0ul, msize, RF_ACTIVE);
357         if (sc->res.mem == NULL) {
358                 device_printf(dev, "0x%lx: Memory range is in use\n", sc->pmem);
359                 sc->hidewin(sc);
360                 goto failed;
361         }
362
363         /* map memory */
364         sc->vmem = pmap_mapdev(sc->pmem, msize);
365         sc->msize = msize;
366
367         DLOG(DIGIDB_INIT, (sc->dev, "internal memory segment 0x%x\n",
368             sc->mem_seg));
369
370         /* Start by resetting the card */
371         reset = FEPRST;
372         if (sc->model == PCXI)
373                 reset |= FEPMEM;
374
375         outb(sc->port, reset);
376         for (i = 0; (inb(sc->port) & FEPMASK) != reset; i++) {
377                 if (i == hz / 10) {
378                         device_printf(dev, "1st reset failed\n");
379                         sc->hidewin(sc);
380                         goto failed;
381                 }
382                 digi_delay(sc, "digirst1", 1);
383         }
384         DLOG(DIGIDB_INIT, (sc->dev, "got reset after %d iterations\n", i));
385
386         if (sc->model != PCXI) {
387                 t = (sc->pmem >> 8) & 0xffe0;
388                 if (sc->model == PCXEVE)
389                         t |= 0x10;              /* enable windowing */
390                 outb(sc->port + 2, t & 0xff);
391                 outb(sc->port + 3, t >> 8);
392         }
393
394         if (sc->model == PCXI || sc->model == PCXE) {
395                 outb(sc->port, FEPRST | FEPMEM);
396                 for (i = 0; (inb(sc->port) & FEPMASK) != FEPRST; i++) {
397                         if (i == hz / 10) {
398                                 device_printf(dev,
399                                     "memory reservation failed (0x%02x)\n",
400                                     inb(sc->port));
401                                 sc->hidewin(sc);
402                                 goto failed;
403                         }
404                         digi_delay(sc, "digirst2", 1);
405                 }
406                 DLOG(DIGIDB_INIT, (sc->dev, "got memory after %d iterations\n",
407                     i));
408         }
409
410         DLOG(DIGIDB_INIT, (sc->dev, "short memory test\n"));
411         ptr = sc->setwin(sc, BOTWIN);
412         vD(ptr) = 0xA55A3CC3;
413         if (vD(ptr) != 0xA55A3CC3) {
414                 device_printf(dev, "1st memory test failed\n");
415                 sc->hidewin(sc);
416                 goto failed;
417         }
418         DLOG(DIGIDB_INIT, (sc->dev, "1st memory test ok\n"));
419
420         ptr = sc->setwin(sc, TOPWIN);
421         vD(ptr) = 0x5AA5C33C;
422         if (vD(ptr) != 0x5AA5C33C) {
423                 device_printf(dev, "2nd memory test failed\n");
424                 sc->hidewin(sc);
425                 goto failed;
426         }
427         DLOG(DIGIDB_INIT, (sc->dev, "2nd memory test ok\n"));
428
429         ptr = sc->setwin(sc, BIOSCODE + ((0xf000 - sc->mem_seg) << 4));
430         vD(ptr) = 0x5AA5C33C;
431         if (vD(ptr) != 0x5AA5C33C) {
432                 device_printf(dev, "3rd (BIOS) memory test failed\n");
433                 sc->hidewin(sc);
434                 goto failed;
435         }
436         DLOG(DIGIDB_INIT, (sc->dev, "3rd memory test ok\n"));
437
438         if ((res = digi_attach(sc)) == 0)
439                 return (0);
440
441 failed:
442         if (sc->res.mem != NULL) {
443                 bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid,
444                     sc->res.mem);
445                 sc->res.mem = NULL;
446         }
447         if (sc->res.io != NULL) {
448                 bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
449                     sc->res.io);
450                 sc->res.io = NULL;
451         }
452
453         return (res);
454 }
455
456 static device_method_t digi_isa_methods[] = {
457         /* Device interface */
458         DEVMETHOD(device_probe, digi_isa_probe),
459         DEVMETHOD(device_attach, digi_isa_attach),
460         DEVMETHOD(device_detach, digi_detach),
461         DEVMETHOD(device_shutdown, digi_shutdown),
462         {0, 0}
463 };
464
465 static driver_t digi_isa_drv = {
466         "digi",
467         digi_isa_methods,
468         sizeof(struct digi_softc),
469 };
470 DRIVER_MODULE(digi, isa, digi_isa_drv, digi_devclass, NULL, NULL);