exca.c revision 90187
1/* $FreeBSD: head/sys/dev/exca/exca.c 90187 2002-02-04 15:55:21Z imp $ */
2
3/*
4 * Copyright (c) 2002 M Warner Losh.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * This software may be derived from NetBSD i82365.c and other files with
27 * the following copyright:
28 *
29 * Copyright (c) 1997 Marc Horowitz.  All rights reserved.
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 *    notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 *    notice, this list of conditions and the following disclaimer in the
38 *    documentation and/or other materials provided with the distribution.
39 * 3. All advertising materials mentioning features or use of this software
40 *    must display the following acknowledgement:
41 *	This product includes software developed by Marc Horowitz.
42 * 4. The name of the author may not be used to endorse or promote products
43 *    derived from this software without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
46 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
47 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
48 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
49 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
50 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
51 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
52 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
54 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55 */
56
57#include <sys/param.h>
58#include <sys/systm.h>
59#include <sys/errno.h>
60#include <sys/kernel.h>
61#include <sys/malloc.h>
62#include <sys/queue.h>
63
64#include <sys/bus.h>
65#include <machine/bus.h>
66#include <sys/rman.h>
67#include <machine/resource.h>
68
69#include <dev/pccard/pccardreg.h>
70#include <dev/pccard/pccardvar.h>
71
72#include <dev/exca/excareg.h>
73#include <dev/exca/excavar.h>
74
75#ifdef EXCA_DEBUG
76#define DEVPRINTF(dev, fmt, args...)	device_printf((dev), (fmt), ## args)
77#define DPRINTF(fmt, args...)		printf(fmt, ## args)
78#else
79#define DEVPRINTF(dev, fmt, args...)
80#define DPRINTF(fmt, args...)
81#endif
82
83
84/* memory */
85
86#define	EXCA_MEMINFO(NUM) {						\
87	EXCA_SYSMEM_ADDR ## NUM ## _START_LSB,				\
88	EXCA_SYSMEM_ADDR ## NUM ## _START_MSB,				\
89	EXCA_SYSMEM_ADDR ## NUM ## _STOP_LSB,				\
90	EXCA_SYSMEM_ADDR ## NUM ## _STOP_MSB,				\
91	EXCA_SYSMEM_ADDR ## NUM ## _WIN,				\
92	EXCA_CARDMEM_ADDR ## NUM ## _LSB,				\
93	EXCA_CARDMEM_ADDR ## NUM ## _MSB,				\
94	EXCA_ADDRWIN_ENABLE_MEM ## NUM ##,				\
95}
96
97static struct mem_map_index_st {
98	int	sysmem_start_lsb;
99	int	sysmem_start_msb;
100	int	sysmem_stop_lsb;
101	int	sysmem_stop_msb;
102	int	sysmem_win;
103	int	cardmem_lsb;
104	int	cardmem_msb;
105	int	memenable;
106} mem_map_index[] = {
107	EXCA_MEMINFO(0),
108	EXCA_MEMINFO(1),
109	EXCA_MEMINFO(2),
110	EXCA_MEMINFO(3),
111	EXCA_MEMINFO(4)
112};
113#undef	EXCA_MEMINFO
114
115/*
116 * Helper function.  This will map the requested memory slot.  We setup the
117 * map before we call this function.  This is used to initially force the
118 * mapping, as well as later restore the mapping after it has been destroyed
119 * in some fashion (due to a power event typically).
120 */
121static void
122exca_do_mem_map(struct exca_softc *sc, int win)
123{
124	struct mem_map_index_st *map;
125	struct pccard_mem_handle *mem;
126
127	map = &mem_map_index[win];
128	mem = &sc->mem[win];
129	exca_write(sc, map->sysmem_start_lsb,
130	    (mem->addr >> EXCA_SYSMEM_ADDRX_SHIFT) & 0xff);
131	exca_write(sc, map->sysmem_start_msb,
132	    ((mem->addr >> (EXCA_SYSMEM_ADDRX_SHIFT + 8)) &
133	    EXCA_SYSMEM_ADDRX_START_MSB_ADDR_MASK) | 0x80);
134
135	exca_write(sc, map->sysmem_stop_lsb,
136	    ((mem->addr + mem->realsize - 1) >>
137	    EXCA_SYSMEM_ADDRX_SHIFT) & 0xff);
138	exca_write(sc, map->sysmem_stop_msb,
139	    (((mem->addr + mem->realsize - 1) >>
140	    (EXCA_SYSMEM_ADDRX_SHIFT + 8)) &
141	    EXCA_SYSMEM_ADDRX_STOP_MSB_ADDR_MASK) |
142	    EXCA_SYSMEM_ADDRX_STOP_MSB_WAIT2);
143
144	exca_write(sc, map->sysmem_win,
145	    (mem->addr >> EXCA_MEMREG_WIN_SHIFT) & 0xff);
146
147	exca_write(sc, map->cardmem_lsb,
148	    (mem->offset >> EXCA_CARDMEM_ADDRX_SHIFT) & 0xff);
149	exca_write(sc, map->cardmem_msb,
150	    ((mem->offset >> (EXCA_CARDMEM_ADDRX_SHIFT + 8)) &
151	    EXCA_CARDMEM_ADDRX_MSB_ADDR_MASK) |
152	    ((mem->kind == PCCARD_MEM_ATTR) ?
153	    EXCA_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0));
154
155	exca_setb(sc, EXCA_ADDRWIN_ENABLE, EXCA_ADDRWIN_ENABLE_MEMCS16 |
156	    map->memenable);
157
158	DELAY(100);
159#ifdef EXCA_DEBUG
160	{
161		int r1, r2, r3, r4, r5, r6, r7;
162		r1 = exca_read(sc, map->sysmem_start_msb);
163		r2 = exca_read(sc, map->sysmem_start_lsb);
164		r3 = exca_read(sc, map->sysmem_stop_msb);
165		r4 = exca_read(sc, map->sysmem_stop_lsb);
166		r5 = exca_read(sc, map->cardmem_msb);
167		r6 = exca_read(sc, map->cardmem_lsb);
168		r7 = exca_read(sc, map->sysmem_win);
169		printf("exca_do_mem_map window %d: %02x%02x %02x%02x "
170		    "%02x%02x %02x (%08x+%08x.%08x*%08lx)\n",
171		    win, r1, r2, r3, r4, r5, r6, r7,
172		    mem->addr, mem->size, mem->realsize,
173		    mem->offset);
174	}
175#endif
176}
177
178/*
179 * public interface to map a resource.  kind is the type of memory to
180 * map (either common or attribute).  Memory created via this interface
181 * starts out at card address 0.  Since the only way to set this is
182 * to set it on a struct resource after it has been mapped, we're safe
183 * in maping this assumption.  Note that resources can be remapped using
184 * exca_do_mem_map so that's how the card address can be set later.
185 */
186int
187exca_mem_map(struct exca_softc *sc, int kind, struct resource *res)
188{
189	int win;
190
191	for (win = 0; win < EXCA_MEM_WINS; win++) {
192		if ((sc->memalloc & (1 << win)) == 0) {
193			sc->memalloc |= (1 << win);
194			break;
195		}
196	}
197	if (win >= EXCA_MEM_WINS)
198		return (1);
199
200	sc->mem[win].cardaddr = 0;
201	sc->mem[win].memt = rman_get_bustag(res);
202	sc->mem[win].memh = rman_get_bushandle(res);
203	sc->mem[win].addr = rman_get_start(res);
204	sc->mem[win].size = rman_get_end(res) - sc->mem[win].addr + 1;
205	sc->mem[win].realsize = sc->mem[win].size + EXCA_MEM_PAGESIZE - 1;
206	sc->mem[win].realsize = sc->mem[win].realsize -
207	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
208	sc->mem[win].offset = (long)(sc->mem[win].addr);
209	sc->mem[win].kind = kind;
210	DPRINTF("exca_mem_map window %d bus %x+%x+%lx card addr %x\n",
211	    win, sc->mem[win].addr, sc->mem[win].size,
212	    sc->mem[win].offset, sc->mem[win].cardaddr);
213	exca_do_mem_map(sc, win);
214
215	return (0);
216}
217
218/*
219 * Private helper function.  This turns off a given memory map that is in
220 * use.  We do this by just clearing the enable bit in the pcic.  If we needed
221 * to make memory unmapping/mapping pairs faster, we would have to store
222 * more state information about the pcic and then use that to intelligently
223 * to the map/unmap.  However, since we don't do that sort of thing often
224 * (generally just at configure time), it isn't a case worth optimizing.
225 */
226static void
227exca_mem_unmap(struct exca_softc *sc, int window)
228{
229	if (window < 0 || window >= EXCA_MEM_WINS)
230		panic("exca_mem_unmap: window out of range");
231
232	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, mem_map_index[window].memenable);
233	sc->memalloc &= ~(1 << window);
234}
235
236/*
237 * Find the map that we're using to hold the resoruce.  This works well
238 * so long as the client drivers don't do silly things like map the same
239 * area mutliple times, or map both common and attribute memory at the
240 * same time.  This latter restriction is a bug.  We likely should just
241 * store a pointer to the res in the mem[x] data structure.
242 */
243static int
244exca_mem_findmap(struct exca_softc *sc, struct resource *res)
245{
246	int win;
247
248	for (win = 0; win < EXCA_MEM_WINS; win++) {
249		if (sc->mem[win].memt == rman_get_bustag(res) &&
250		    sc->mem[win].addr == rman_get_start(res) &&
251		    sc->mem[win].size == rman_get_size(res))
252			return (win);
253	}
254	return (-1);
255}
256
257/*
258 * Set the memory flag.  This means that we are setting if the memory
259 * is coming from attribute memory or from common memory on the card.
260 * CIS entries are generally in attribute memory (although they can
261 * reside in common memory).  Generally, this is the only use for attribute
262 * memory.  However, some cards require their drivers to dance in both
263 * common and/or attribute memory and this interface (and setting the
264 * offset interface) exist for such cards.
265 */
266int
267exca_mem_set_flags(struct exca_softc *sc, struct resource *res, uint32_t flags)
268{
269	int win;
270
271	win = exca_mem_findmap(sc, res);
272	if (win < 0) {
273		device_printf(sc->dev,
274		    "set_res_flags: specified resource not active\n");
275		return (ENOENT);
276	}
277
278	sc->mem[win].kind = flags;
279	exca_do_mem_map(sc, win);
280	return (0);
281}
282
283/*
284 * Given a resource, go ahead and unmap it if we can find it in the
285 * resrouce list that's used.
286 */
287int
288exca_mem_unmap_res(struct exca_softc *sc, struct resource *res)
289{
290	int win;
291
292	win = exca_mem_findmap(sc, res);
293	if (win < 0)
294		return (ENOENT);
295	exca_mem_unmap(sc, win);
296	return (0);
297}
298
299/*
300 * Set the offset of the memory.  We use this for reading the CIS and
301 * frobbing the pccard's pccard registers (POR, etc).  Some drivers
302 * need to access this functionality as well, since they have receive
303 * buffers defined in the attribute memory.  Thankfully, these cards
304 * are few and fare between.  Some cards also have common memory that
305 * is large and only map a small portion of it at a time (but these cards
306 * are rare, the more common case being to have just a small amount
307 * of common memory that the driver needs to bcopy data from in order to
308 * get at it.
309 */
310int
311exca_mem_set_offset(struct exca_softc *sc, struct resource *res,
312    uint32_t cardaddr, uint32_t *deltap)
313{
314	int win;
315	uint32_t delta;
316
317	win = exca_mem_findmap(sc, res);
318	if (win < 0) {
319		device_printf(sc->dev,
320		    "set_memory_offset: specified resource not active\n");
321		return (ENOENT);
322	}
323	sc->mem[win].cardaddr = cardaddr;
324	delta = cardaddr % EXCA_MEM_PAGESIZE;
325	if (deltap)
326		*deltap = delta;
327	cardaddr -= delta;
328	sc->mem[win].realsize = sc->mem[win].size + delta +
329	    EXCA_MEM_PAGESIZE - 1;
330	sc->mem[win].realsize = sc->mem[win].realsize -
331	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
332	sc->mem[win].offset = cardaddr - sc->mem[win].addr;
333	exca_do_mem_map(sc, win);
334	return (0);
335}
336
337
338/* I/O */
339
340#define	EXCA_IOINFO(NUM) {						\
341	EXCA_IOADDR ## NUM ## _START_LSB,				\
342	EXCA_IOADDR ## NUM ## _START_MSB,				\
343	EXCA_IOADDR ## NUM ## _STOP_LSB,				\
344	EXCA_IOADDR ## NUM ## _STOP_MSB,				\
345	EXCA_ADDRWIN_ENABLE_IO ## NUM ##,				\
346	EXCA_IOCTL_IO ## NUM ## _WAITSTATE				\
347	| EXCA_IOCTL_IO ## NUM ## _ZEROWAIT				\
348	| EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_MASK			\
349	| EXCA_IOCTL_IO ## NUM ## _DATASIZE_MASK,			\
350	{								\
351		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_CARD,		\
352		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
353		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_8BIT,		\
354		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
355		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_16BIT,		\
356	}								\
357}
358
359static struct io_map_index_st {
360	int	start_lsb;
361	int	start_msb;
362	int	stop_lsb;
363	int	stop_msb;
364	int	ioenable;
365	int	ioctlmask;
366	int	ioctlbits[3]; /* indexed by PCCARD_WIDTH_* */
367} io_map_index[] = {
368	EXCA_IOINFO(0),
369	EXCA_IOINFO(1),
370};
371#undef	EXCA_IOINFO
372
373static void
374exca_do_io_map(struct exca_softc *sc, int win)
375{
376	struct io_map_index_st *map;
377
378	struct pccard_io_handle *io;
379
380	map = &io_map_index[win];
381	io = &sc->io[win];
382	exca_write(sc, map->start_lsb, io->addr & 0xff);
383	exca_write(sc, map->start_msb, (io->addr >> 8) & 0xff);
384
385	exca_write(sc, map->stop_lsb, (io->addr + io->size - 1) & 0xff);
386	exca_write(sc, map->stop_msb, ((io->addr + io->size - 1) >> 8) & 0xff);
387
388	exca_clrb(sc, EXCA_IOCTL, map->ioctlmask);
389	exca_setb(sc, EXCA_IOCTL, map->ioctlbits[io->width]);
390
391	exca_setb(sc, EXCA_ADDRWIN_ENABLE, map->ioenable);
392#ifdef EXCA_DEBUG
393	{
394		int r1, r2, r3, r4;
395		r1 = exca_read(sc, map->start_msb);
396		r2 = exca_read(sc, map->start_lsb);
397		r3 = exca_read(sc, map->stop_msb);
398		r4 = exca_read(sc, map->stop_lsb);
399		DPRINTF("exca_do_io_map window %d: %02x%02x %02x%02x "
400		    "(%08x+%08x)\n", win, r1, r2, r3, r4,
401		    io->addr, io->size);
402	}
403#endif
404}
405
406int
407exca_io_map(struct exca_softc *sc, int width, struct resource *r)
408{
409	int win;
410#ifdef EXCA_DEBUG
411	static char *width_names[] = { "auto", "io8", "io16"};
412#endif
413	for (win=0; win < EXCA_IO_WINS; win++) {
414		if ((sc->ioalloc & (1 << win)) == 0) {
415			sc->ioalloc |= (1 << win);
416			break;
417		}
418	}
419	if (win >= EXCA_IO_WINS)
420		return (1);
421
422	sc->io[win].iot = rman_get_bustag(r);
423	sc->io[win].ioh = rman_get_bushandle(r);
424	sc->io[win].addr = rman_get_start(r);
425	sc->io[win].size = rman_get_end(r) - sc->io[win].addr + 1;
426	sc->io[win].flags = 0;
427	sc->io[win].width = width;
428	DPRINTF("exca_io_map window %d %s port %x+%x\n",
429	    win, width_names[width], sc->io[win].addr,
430	    sc->io[win].size);
431	exca_do_io_map(sc, win);
432
433	return (0);
434}
435
436static void
437exca_io_unmap(struct exca_softc *sc, int window)
438{
439	if (window >= EXCA_IO_WINS)
440		panic("exca_io_unmap: window out of range");
441
442	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, io_map_index[window].ioenable);
443
444	sc->ioalloc &= ~(1 << window);
445
446	sc->io[window].iot = 0;
447	sc->io[window].ioh = 0;
448	sc->io[window].addr = 0;
449	sc->io[window].size = 0;
450	sc->io[window].flags = 0;
451	sc->io[window].width = 0;
452}
453
454static int
455exca_io_findmap(struct exca_softc *sc, struct resource *res)
456{
457	int win;
458
459	for (win = 0; win < EXCA_IO_WINS; win++) {
460		if (sc->io[win].iot == rman_get_bustag(res) &&
461		    sc->io[win].addr == rman_get_start(res) &&
462		    sc->io[win].size == rman_get_size(res))
463			return (win);
464	}
465	return (-1);
466}
467
468
469int
470exca_io_unmap_res(struct exca_softc *sc, struct resource *res)
471{
472	int win;
473
474	win = exca_io_findmap(sc, res);
475	if (win < 0)
476		return (ENOENT);
477	exca_io_unmap(sc, win);
478	return (0);
479}
480
481/* Misc */
482
483/*
484 * If interrupts are enabled, then we should be able to just wait for
485 * an interrupt routine to wake us up.  Busy waiting shouldn't be
486 * necessary.  Sadly, not all legacy ISA cards support an interrupt
487 * for the busy state transitions, at least according to their datasheets,
488 * so we busy wait a while here..
489 */
490static void
491exca_wait_ready(struct exca_softc *sc)
492{
493	int i;
494	DEVPRINTF(sc->dev, "exca_wait_ready: status 0x%02x\n",
495	    exca_read(sc, EXCA_IF_STATUS));
496	for (i = 0; i < 10000; i++) {
497		if (exca_read(sc, EXCA_IF_STATUS) & EXCA_IF_STATUS_READY)
498			return;
499		DELAY(500);
500	}
501	device_printf(sc->dev, "ready never happened, status = %02x\n",
502	    exca_read(sc, EXCA_IF_STATUS));
503}
504
505/*
506 * Reset the card.  Ideally, we'd do a lot of this via interrupts.
507 * However, many PC Cards will deassert the ready signal.  This means
508 * that they are asserting an interrupt.  This makes it hard to
509 * do anything but a busy wait here.  One could argue that these
510 * such cards are broken, or that the bridge that allows this sort
511 * of interrupt through isn't quite what you'd want (and may be a standards
512 * violation).  However, such arguing would leave a huge class of pc cards
513 * and bridges out of reach for
514 */
515void
516exca_reset(struct exca_softc *sc, device_t child)
517{
518	int cardtype;
519	int win;
520
521	/* enable socket i/o */
522	exca_setb(sc, EXCA_PWRCTL, EXCA_PWRCTL_OE);
523
524	exca_write(sc, EXCA_INTR, EXCA_INTR_ENABLE);
525	/* hold reset for 30ms */
526	DELAY(30*1000);
527	/* clear the reset flag */
528	exca_setb(sc, EXCA_INTR, EXCA_INTR_RESET);
529	/* wait 20ms as per pc card standard (r2.01) section 4.3.6 */
530	DELAY(20*1000);
531
532	exca_wait_ready(sc);
533
534	/* disable all address windows */
535	exca_write(sc, EXCA_ADDRWIN_ENABLE, 0);
536
537	CARD_GET_TYPE(child, &cardtype);
538	exca_setb(sc, EXCA_INTR, (cardtype == PCCARD_IFTYPE_IO) ?
539	    EXCA_INTR_CARDTYPE_IO : EXCA_INTR_CARDTYPE_MEM);
540	DEVPRINTF(sc->dev, "card type is %s\n",
541	    (cardtype == PCCARD_IFTYPE_IO) ? "io" : "mem");
542
543	/* reinstall all the memory and io mappings */
544	for (win = 0; win < EXCA_MEM_WINS; ++win)
545		if (sc->memalloc & (1 << win))
546			exca_do_mem_map(sc, win);
547	for (win = 0; win < EXCA_IO_WINS; ++win)
548		if (sc->ioalloc & (1 << win))
549			exca_do_io_map(sc, win);
550}
551
552/*
553 * Initialize the exca_softc data structure for the first time.
554 */
555void
556exca_init(struct exca_softc *sc, device_t dev, exca_write_t *wrfn,
557    exca_read_t *rdfn, bus_space_tag_t bst, bus_space_handle_t bsh,
558    uint32_t offset)
559{
560	sc->dev = dev;
561	sc->write_exca = wrfn;
562	sc->read_exca = rdfn;
563	sc->memalloc = 0;
564	sc->ioalloc = 0;
565	sc->bst = bst;
566	sc->bsh = bsh;
567	sc->offset = offset;
568	sc->flags = 0;
569}
570
571/*
572 * Probe the expected slots.  We maybe should set the ID for each of these
573 * slots too while we're at it.  But maybe that belongs to a separate
574 * function.
575 *
576 * Callers must charantee that there are at least EXCA_NSLOTS (4) in
577 * the array that they pass the address of the first element in the
578 * "exca" parameter.
579 */
580int
581exca_probe_slots(device_t dev, struct exca_softc *exca, exca_write_t writefnp,
582    exca_read_t readfnp)
583{
584	int rid;
585	struct resource *res;
586	int err;
587	bus_space_tag_t iot;
588	bus_space_handle_t ioh;
589	int i;
590
591	err = ENXIO;
592	rid = 0;
593	res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, EXCA_IOSIZE,
594	    RF_ACTIVE);
595	if (res == NULL)
596		return (ENXIO);
597	iot = rman_get_bustag(res);
598	ioh = rman_get_bushandle(res);
599	for (i = 0; i < EXCA_NSLOTS; i++) {
600		exca_init(&exca[i], dev, writefnp, readfnp, iot, ioh,
601		    i * EXCA_SOCKET_SIZE);
602		if (exca_is_pcic(&exca[i])) {
603			err = 0;
604			exca[i].flags |= EXCA_SOCKET_PRESENT;
605		}
606	}
607	bus_release_resource(dev, SYS_RES_IOPORT, rid, res);
608	return (err);
609}
610
611int
612exca_is_pcic(struct exca_softc *sc)
613{
614	/* XXX */
615	return (0);
616}
617