xref: /NextBSD/sys/dev/digi/digi_isa.c (revision 39c8125591b55233b6903033f1555880a8cd2725)
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 *
digi_isa_setwin(struct digi_softc * sc,unsigned int addr)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 *
digi_xi_setwin(struct digi_softc * sc,unsigned int addr)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
digi_isa_hidewin(struct digi_softc * sc)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
digi_isa_towin(struct digi_softc * sc,int win)95 digi_isa_towin(struct digi_softc *sc, int win)
96 {
97 	outb(sc->wport, sc->window = win);
98 }
99 
100 static void
digi_xi_towin(struct digi_softc * sc,int win)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
digi_isa_check(struct digi_softc * sc)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
digi_isa_probe(device_t dev)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, 0);
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
digi_isa_attach(device_t dev)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);
475