Merge from vendor branch LIBARCHIVE:
[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  * $DragonFly: src/sys/dev/serial/digi/digi_isa.c,v 1.2 2006/10/25 20:56:01 dillon Exp $
31  */
32
33 /*-
34  * TODO:
35  *      Figure out how to make the non-Xi boards use memory addresses other
36  *      than 0xd0000 !!!
37  */
38
39 #include <sys/param.h>
40
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/tty.h>
44 #include <sys/bus.h>
45 #include <sys/rman.h>
46
47 #include <vm/vm.h>
48 #include <vm/pmap.h>
49
50 #include <dev/serial/digi/digiio.h>
51 #include <dev/serial/digi/digireg.h>
52 #include <dev/serial/digi/digi.h>
53
54 /* Valid i/o addresses are any of these with either 0 or 4 added */
55 static u_long digi_validio[] = {
56         0x100, 0x110, 0x120, 0x200, 0x220, 0x300, 0x320
57 };
58 #define DIGI_NVALIDIO   (sizeof(digi_validio) / sizeof(digi_validio[0]))
59 #define IO_SIZE         0x04
60
61 static u_long digi_validmem[] = {
62         0x80000, 0x88000, 0x90000, 0x98000, 0xa0000, 0xa8000, 0xb0000, 0xb8000,
63         0xc0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000, 0xe8000, 0xf0000, 0xf8000,
64         0xf0000000, 0xf1000000, 0xf2000000, 0xf3000000, 0xf4000000, 0xf5000000,
65         0xf6000000, 0xf7000000, 0xf8000000, 0xf9000000, 0xfa000000, 0xfb000000,
66         0xfc000000, 0xfd000000, 0xfe000000, 0xff000000
67 };
68 #define DIGI_NVALIDMEM  (sizeof(digi_validmem) / sizeof(digi_validmem[0]))
69
70 static u_char *
71 digi_isa_setwin(struct digi_softc *sc, unsigned int addr)
72 {
73         outb(sc->wport, sc->window = FEPWIN | (addr >> sc->win_bits));
74         return (sc->vmem + (addr % sc->win_size));
75 }
76
77 static u_char *
78 digi_xi_setwin(struct digi_softc *sc, unsigned int addr)
79 {
80         outb(sc->wport, sc->window = FEPMEM);
81         return (sc->vmem + addr);
82 }
83
84 static void
85 digi_isa_hidewin(struct digi_softc *sc)
86 {
87         outb(sc->wport, sc->window = 0);
88         /* outb(sc->port, 0); */
89 }
90
91 static void
92 digi_isa_towin(struct digi_softc *sc, int win)
93 {
94         outb(sc->wport, sc->window = win);
95 }
96
97 static void
98 digi_xi_towin(struct digi_softc *sc, int win)
99 {
100         outb(sc->wport, sc->window = FEPMEM);
101 }
102
103 /*
104  * sc->port should be set and its resource allocated.
105  */
106 static int
107 digi_isa_check(struct digi_softc *sc)
108 {
109         int i, ident;
110
111         sc->name = NULL;
112
113         /* Invasive probe - reset the card */
114         outb(sc->port, FEPRST);
115         for (i = 0; (inb(sc->port) & FEPMASK) != FEPRST; i++) {
116                 if (i == hz / 10)
117                         return (0);
118                 digi_delay(sc, "digirst", 1);
119         }
120         DLOG(DIGIDB_INIT, (sc->dev, "got reset after %d iterations\n", i));
121
122         ident = inb(sc->port);
123
124         /*
125          * NOTE, this probe is all wrong.  I haven't got the data sheets !
126          */
127
128         DLOG(DIGIDB_INIT, (sc->dev, "board type is 0x%x\n", ident));
129         if (ident & 0x1) {
130                 switch (ident) {
131                 case 0x05:
132                 case 0x15:
133                 case 0x25:
134                 case 0x35:
135                         sc->model = PCXI;
136                         sc->csigs = &digi_xixe_signals;
137                         switch (ident & 0x30) {
138                         case 0:
139                                 sc->name = "Digiboard PC/Xi 64K";
140                                 sc->mem_seg = 0xf000;
141                                 sc->win_size = 0x10000;
142                                 sc->win_bits = 16;
143                                 break;
144                         case 0x10:
145                                 sc->name = "Digiboard PC/Xi 128K";
146                                 sc->mem_seg = 0xE000;
147                                 sc->win_size = 0x20000;
148                                 sc->win_bits = 17;
149                                 break;
150                         case 0x20:
151                                 sc->name = "Digiboard PC/Xi 256K";
152                                 sc->mem_seg = 0xC000;
153                                 sc->win_size = 0x40000;
154                                 sc->win_bits = 18;
155                                 break;
156                         case 0x30:
157                                 sc->name = "Digiboard PC/Xi 512K";
158                                 sc->mem_seg = 0x8000;
159                                 sc->win_size = 0x80000;
160                                 sc->win_bits = 19;
161                                 break;
162                         }
163                         sc->wport = sc->port;
164                         sc->module = "Xe";
165
166                         sc->setwin = digi_xi_setwin;
167                         sc->hidewin = digi_isa_hidewin;
168                         sc->towin = digi_xi_towin;
169                         break;
170
171                 case 0xf5:
172                         sc->name = "Digiboard PC/Xem";
173                         sc->model = PCXEM;
174                         sc->csigs = &digi_normal_signals;
175                         sc->win_size = 0x8000;
176                         sc->win_bits = 15;
177                         sc->wport = sc->port + 1;
178                         sc->module = "Xem";
179
180                         sc->setwin = digi_isa_setwin;
181                         sc->hidewin = digi_isa_hidewin;
182                         sc->towin = digi_isa_towin;
183                         break;
184                 }
185         } else {
186                 outb(sc->port, 1);
187                 ident = inb(sc->port);
188
189                 if (ident & 0x1) {
190                         device_printf(sc->dev, "PC/Xm is unsupported\n");
191                         return (0);
192                 }
193
194                 sc->mem_seg = 0xf000;
195
196                 if (!(ident & 0xc0)) {
197                         sc->name = "Digiboard PC/Xe 64K";
198                         sc->model = PCXE;
199                         sc->csigs = &digi_xixe_signals;
200                         sc->win_size = 0x10000;
201                         sc->win_bits = 16;
202                         sc->wport = sc->port;
203                 } else {
204                         sc->name = "Digiboard PC/Xe 64/8K (windowed)";
205                         sc->model = PCXEVE;
206                         sc->csigs = &digi_normal_signals;
207                         sc->win_size = 0x2000;
208                         sc->win_bits = 13;
209                         sc->wport = sc->port + 1;
210                 }
211                 sc->module = "Xe";
212
213                 sc->setwin = digi_isa_setwin;
214                 sc->hidewin = digi_isa_hidewin;
215                 sc->towin = digi_isa_towin;
216         }
217
218         return (sc->name != NULL);
219 }
220
221 static int
222 digi_isa_probe(device_t dev)
223 {
224         struct digi_softc *sc = device_get_softc(dev);
225         int i;
226
227         KASSERT(sc, ("digi%d: softc not allocated in digi_isa_probe\n",
228             device_get_unit(dev)));
229
230         bzero(sc, sizeof(*sc));
231         sc->status = DIGI_STATUS_NOTINIT;
232         sc->dev = dev;
233         sc->res.unit = device_get_unit(dev);
234         if (sc->res.unit >= 16) {
235                 /* Don't overflow our control mask */
236                 device_printf(dev, "At most 16 digiboards may be used\n");
237                 return (ENXIO);
238         }
239         DLOG(DIGIDB_INIT, (sc->dev, "probing on isa bus\n"));
240
241         /* Check that we've got a valid i/o address */
242         if ((sc->port = bus_get_resource_start(dev, SYS_RES_IOPORT, 0)) == 0) {
243                 DLOG(DIGIDB_INIT, (sc->dev, "io address not given\n"));
244                 return (ENXIO);
245         }
246         for (i = 0; i < DIGI_NVALIDIO; i++)
247                 if (sc->port == digi_validio[i] ||
248                     sc->port == digi_validio[i] + 4)
249                         break;
250         if (i == DIGI_NVALIDIO) {
251                 device_printf(dev, "0x%03x: Invalid i/o address\n", sc->port);
252                 return (ENXIO);
253         }
254
255         /* Ditto for our memory address */
256         if ((sc->pmem = bus_get_resource_start(dev, SYS_RES_MEMORY, 0)) == 0)
257                 return (ENXIO);
258         for (i = 0; i < DIGI_NVALIDMEM; i++)
259                 if (sc->pmem == digi_validmem[i])
260                         break;
261         if (i == DIGI_NVALIDMEM) {
262                 device_printf(dev, "0x%lx: Invalid memory address\n", sc->pmem);
263                 return (ENXIO);
264         }
265         if ((sc->pmem & 0xfffffful) != sc->pmem) {
266                 device_printf(dev, "0x%lx: Memory address not supported\n",
267                     sc->pmem);
268                 return (ENXIO);
269         }
270         sc->vmem = (u_char *)sc->pmem;
271
272         DLOG(DIGIDB_INIT, (sc->dev, "isa? port 0x%03x mem 0x%lx\n",
273             sc->port, sc->pmem));
274
275         /* Temporarily map our io ports */
276         sc->res.iorid = 0;
277         sc->res.io = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->res.iorid,
278             0ul, ~0ul, IO_SIZE, RF_ACTIVE);
279         if (sc->res.io == NULL)
280                 return (ENXIO);
281
282         /* Check the type of card and get internal memory characteristics */
283         if (!digi_isa_check(sc)) {
284                 bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
285                     sc->res.io);
286                 return (ENXIO);
287         }
288
289         /* Temporarily map our memory */
290         sc->res.mrid = 0;
291         sc->res.mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->res.mrid,
292             0ul, ~0ul, sc->win_size, RF_ALLOCATED);
293         if (sc->res.mem == NULL) {
294                 device_printf(dev, "0x%lx: Memory range is in use\n", sc->pmem);
295                 bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid,
296                     sc->res.io);
297                 return (ENXIO);
298         }
299
300         outb(sc->port, FEPCLR);         /* drop RESET */
301         sc->hidewin(sc);        /* set initial sc->window */
302
303         bus_release_resource(dev, SYS_RES_MEMORY, sc->res.mrid, sc->res.mem);
304         bus_release_resource(dev, SYS_RES_IOPORT, sc->res.iorid, sc->res.io);
305
306         /* Let digi_isa_attach() know what we've found */
307         bus_set_resource(dev, SYS_RES_IOPORT, 0, sc->port, IO_SIZE);
308         bus_set_resource(dev, SYS_RES_MEMORY, 0, sc->pmem, sc->win_size);
309
310         DLOG(DIGIDB_INIT, (sc->dev, "Probe returns -10\n"));
311
312         return (-10);           /* Other drivers are preferred for now */
313 }
314
315 static int
316 digi_isa_attach(device_t dev)
317 {
318         struct digi_softc *sc = device_get_softc(dev);
319         int i, t, res;
320         u_char *ptr;
321         int reset;
322         u_long msize, iosize;
323         long scport;
324
325         KASSERT(sc, ("digi%d: softc not allocated in digi_isa_attach\n",
326             device_get_unit(dev)));
327
328         res = ENXIO;
329         bzero(sc, sizeof(*sc));
330         sc->status = DIGI_STATUS_NOTINIT;
331         sc->dev = dev;
332         sc->res.unit = device_get_unit(dev);
333         DLOG(DIGIDB_INIT, (sc->dev, "attaching\n"));
334
335         bus_get_resource(dev, SYS_RES_IOPORT, 0, &scport, &iosize);
336         bus_get_resource(dev, SYS_RES_MEMORY, 0, &sc->pmem, &msize);
337         sc->port = scport;
338         /* sc->altpin = !!(device_get_flags(dev) & DGBFLAG_ALTPIN); */
339
340         /* Allocate resources (verified in digi_isa_probe()) */
341         sc->res.iorid = 0;
342         sc->res.io = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->res.iorid,
343             0ul, ~0ul, iosize, RF_ACTIVE);
344         if (sc->res.io == NULL)
345                 return (ENXIO);
346
347         /* Check the type of card and get internal memory characteristics */
348         DLOG(DIGIDB_INIT, (sc->dev, "Checking card type\n"));
349         if (!digi_isa_check(sc))
350                 goto failed;
351
352         callout_init(&sc->callout);
353         callout_init(&sc->inttest);
354
355         sc->res.mrid = 0;
356         sc->res.mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->res.mrid,
357             0ul, ~0ul, msize, RF_ACTIVE);
358         if (sc->res.mem == NULL) {
359                 device_printf(dev, "0x%lx: Memory range is in use\n", sc->pmem);
360                 sc->hidewin(sc);
361                 goto failed;
362         }
363
364         /* map memory */
365         sc->vmem = pmap_mapdev(sc->pmem, 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, 0, 0);