More minor IPI work.
[dragonfly/vkernel-mp.git] / sys / dev / serial / digi / digi_isa.c
blobfeeb230a3ad7d6f040f390b69b8fed170b6b5c14
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.
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.
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.
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 22:56:01 dillon Exp $
33 /*-
34 * TODO:
35 * Figure out how to make the non-Xi boards use memory addresses other
36 * than 0xd0000 !!!
39 #include <sys/param.h>
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>
47 #include <vm/vm.h>
48 #include <vm/pmap.h>
50 #include <dev/serial/digi/digiio.h>
51 #include <dev/serial/digi/digireg.h>
52 #include <dev/serial/digi/digi.h>
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
58 #define DIGI_NVALIDIO (sizeof(digi_validio) / sizeof(digi_validio[0]))
59 #define IO_SIZE 0x04
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
68 #define DIGI_NVALIDMEM (sizeof(digi_validmem) / sizeof(digi_validmem[0]))
70 static u_char *
71 digi_isa_setwin(struct digi_softc *sc, unsigned int addr)
73 outb(sc->wport, sc->window = FEPWIN | (addr >> sc->win_bits));
74 return (sc->vmem + (addr % sc->win_size));
77 static u_char *
78 digi_xi_setwin(struct digi_softc *sc, unsigned int addr)
80 outb(sc->wport, sc->window = FEPMEM);
81 return (sc->vmem + addr);
84 static void
85 digi_isa_hidewin(struct digi_softc *sc)
87 outb(sc->wport, sc->window = 0);
88 /* outb(sc->port, 0); */
91 static void
92 digi_isa_towin(struct digi_softc *sc, int win)
94 outb(sc->wport, sc->window = win);
97 static void
98 digi_xi_towin(struct digi_softc *sc, int win)
100 outb(sc->wport, sc->window = FEPMEM);
104 * sc->port should be set and its resource allocated.
106 static int
107 digi_isa_check(struct digi_softc *sc)
109 int i, ident;
111 sc->name = NULL;
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);
120 DLOG(DIGIDB_INIT, (sc->dev, "got reset after %d iterations\n", i));
122 ident = inb(sc->port);
125 * NOTE, this probe is all wrong. I haven't got the data sheets !
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;
163 sc->wport = sc->port;
164 sc->module = "Xe";
166 sc->setwin = digi_xi_setwin;
167 sc->hidewin = digi_isa_hidewin;
168 sc->towin = digi_xi_towin;
169 break;
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";
180 sc->setwin = digi_isa_setwin;
181 sc->hidewin = digi_isa_hidewin;
182 sc->towin = digi_isa_towin;
183 break;
185 } else {
186 outb(sc->port, 1);
187 ident = inb(sc->port);
189 if (ident & 0x1) {
190 device_printf(sc->dev, "PC/Xm is unsupported\n");
191 return (0);
194 sc->mem_seg = 0xf000;
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;
211 sc->module = "Xe";
213 sc->setwin = digi_isa_setwin;
214 sc->hidewin = digi_isa_hidewin;
215 sc->towin = digi_isa_towin;
218 return (sc->name != NULL);
221 static int
222 digi_isa_probe(device_t dev)
224 struct digi_softc *sc = device_get_softc(dev);
225 int i;
227 KASSERT(sc, ("digi%d: softc not allocated in digi_isa_probe\n",
228 device_get_unit(dev)));
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);
239 DLOG(DIGIDB_INIT, (sc->dev, "probing on isa bus\n"));
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);
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);
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);
265 if ((sc->pmem & 0xfffffful) != sc->pmem) {
266 device_printf(dev, "0x%lx: Memory address not supported\n",
267 sc->pmem);
268 return (ENXIO);
270 sc->vmem = (u_char *)sc->pmem;
272 DLOG(DIGIDB_INIT, (sc->dev, "isa? port 0x%03x mem 0x%lx\n",
273 sc->port, sc->pmem));
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);
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);
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);
300 outb(sc->port, FEPCLR); /* drop RESET */
301 sc->hidewin(sc); /* set initial sc->window */
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);
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);
310 DLOG(DIGIDB_INIT, (sc->dev, "Probe returns -10\n"));
312 return (-10); /* Other drivers are preferred for now */
315 static int
316 digi_isa_attach(device_t dev)
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;
325 KASSERT(sc, ("digi%d: softc not allocated in digi_isa_attach\n",
326 device_get_unit(dev)));
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"));
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); */
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);
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;
352 callout_init(&sc->callout);
353 callout_init(&sc->inttest);
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;
364 /* map memory */
365 sc->vmem = pmap_mapdev(sc->pmem, msize);
367 DLOG(DIGIDB_INIT, (sc->dev, "internal memory segment 0x%x\n",
368 sc->mem_seg));
370 /* Start by resetting the card */
371 reset = FEPRST;
372 if (sc->model == PCXI)
373 reset |= FEPMEM;
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;
382 digi_delay(sc, "digirst1", 1);
384 DLOG(DIGIDB_INIT, (sc->dev, "got reset after %d iterations\n", i));
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);
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;
404 digi_delay(sc, "digirst2", 1);
406 DLOG(DIGIDB_INIT, (sc->dev, "got memory after %d iterations\n",
407 i));
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;
418 DLOG(DIGIDB_INIT, (sc->dev, "1st memory test ok\n"));
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;
427 DLOG(DIGIDB_INIT, (sc->dev, "2nd memory test ok\n"));
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;
436 DLOG(DIGIDB_INIT, (sc->dev, "3rd memory test ok\n"));
438 if ((res = digi_attach(sc)) == 0)
439 return (0);
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;
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;
453 return (res);
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}
465 static driver_t digi_isa_drv = {
466 "digi",
467 digi_isa_methods,
468 sizeof(struct digi_softc),
470 DRIVER_MODULE(digi, isa, digi_isa_drv, digi_devclass, 0, 0);