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