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