xref: /freebsd/sys/dev/exca/exca.c (revision 3494f7c0)
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause AND BSD-2-Clause
3  *
4  * Copyright (c) 2002-2005 M. Warner Losh <imp@FreeBSD.org>
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/condvar.h>
60 #include <sys/errno.h>
61 #include <sys/kernel.h>
62 #include <sys/malloc.h>
63 #include <sys/queue.h>
64 #include <sys/module.h>
65 #include <sys/lock.h>
66 #include <sys/mutex.h>
67 #include <sys/conf.h>
68 
69 #include <sys/bus.h>
70 #include <machine/bus.h>
71 #include <sys/rman.h>
72 #include <machine/resource.h>
73 
74 #include <dev/pccard/pccardreg.h>
75 #include <dev/pccard/pccardvar.h>
76 
77 #include <dev/exca/excareg.h>
78 #include <dev/exca/excavar.h>
79 
80 #ifdef EXCA_DEBUG
81 #define DEVPRINTF(dev, fmt, args...)	device_printf((dev), (fmt), ## args)
82 #define DPRINTF(fmt, args...)		printf(fmt, ## args)
83 #else
84 #define DEVPRINTF(dev, fmt, args...)
85 #define DPRINTF(fmt, args...)
86 #endif
87 
88 static const char *chip_names[] =
89 {
90 	"CardBus socket",
91 	"Intel i82365SL-A/B or clone",
92 	"Intel i82365sl-DF step",
93 	"VLSI chip",
94 	"Cirrus Logic PD6710",
95 	"Cirrus logic PD6722",
96 	"Cirrus Logic PD6729",
97 	"Vadem 365",
98 	"Vadem 465",
99 	"Vadem 468",
100 	"Vadem 469",
101 	"Ricoh RF5C296",
102 	"Ricoh RF5C396",
103 	"IBM clone",
104 	"IBM KING PCMCIA Controller"
105 };
106 
107 static exca_getb_fn exca_mem_getb;
108 static exca_putb_fn exca_mem_putb;
109 static exca_getb_fn exca_io_getb;
110 static exca_putb_fn exca_io_putb;
111 
112 /* memory */
113 
114 #define	EXCA_MEMINFO(NUM) {						\
115 	EXCA_SYSMEM_ADDR ## NUM ## _START_LSB,				\
116 	EXCA_SYSMEM_ADDR ## NUM ## _START_MSB,				\
117 	EXCA_SYSMEM_ADDR ## NUM ## _STOP_LSB,				\
118 	EXCA_SYSMEM_ADDR ## NUM ## _STOP_MSB,				\
119 	EXCA_SYSMEM_ADDR ## NUM ## _WIN,				\
120 	EXCA_CARDMEM_ADDR ## NUM ## _LSB,				\
121 	EXCA_CARDMEM_ADDR ## NUM ## _MSB,				\
122 	EXCA_ADDRWIN_ENABLE_MEM ## NUM,					\
123 }
124 
125 static struct mem_map_index_st {
126 	int	sysmem_start_lsb;
127 	int	sysmem_start_msb;
128 	int	sysmem_stop_lsb;
129 	int	sysmem_stop_msb;
130 	int	sysmem_win;
131 	int	cardmem_lsb;
132 	int	cardmem_msb;
133 	int	memenable;
134 } mem_map_index[] = {
135 	EXCA_MEMINFO(0),
136 	EXCA_MEMINFO(1),
137 	EXCA_MEMINFO(2),
138 	EXCA_MEMINFO(3),
139 	EXCA_MEMINFO(4)
140 };
141 #undef	EXCA_MEMINFO
142 
143 static uint8_t
144 exca_mem_getb(struct exca_softc *sc, int reg)
145 {
146 	return (bus_space_read_1(sc->bst, sc->bsh, sc->offset + reg));
147 }
148 
149 static void
150 exca_mem_putb(struct exca_softc *sc, int reg, uint8_t val)
151 {
152 	bus_space_write_1(sc->bst, sc->bsh, sc->offset + reg, val);
153 }
154 
155 static uint8_t
156 exca_io_getb(struct exca_softc *sc, int reg)
157 {
158 	bus_space_write_1(sc->bst, sc->bsh, EXCA_REG_INDEX, reg + sc->offset);
159 	return (bus_space_read_1(sc->bst, sc->bsh, EXCA_REG_DATA));
160 }
161 
162 static void
163 exca_io_putb(struct exca_softc *sc, int reg, uint8_t val)
164 {
165 	bus_space_write_1(sc->bst, sc->bsh, EXCA_REG_INDEX, reg + sc->offset);
166 	bus_space_write_1(sc->bst, sc->bsh, EXCA_REG_DATA, val);
167 }
168 
169 /*
170  * Helper function.  This will map the requested memory slot.  We setup the
171  * map before we call this function.  This is used to initially force the
172  * mapping, as well as later restore the mapping after it has been destroyed
173  * in some fashion (due to a power event typically).
174  */
175 static void
176 exca_do_mem_map(struct exca_softc *sc, int win)
177 {
178 	struct mem_map_index_st *map;
179 	struct pccard_mem_handle *mem;
180 	uint32_t offset;
181 	uint32_t mem16;
182 	uint32_t attrmem;
183 
184 	map = &mem_map_index[win];
185 	mem = &sc->mem[win];
186 	mem16 = (mem->kind & PCCARD_MEM_16BIT) ?
187 	    EXCA_SYSMEM_ADDRX_START_MSB_DATASIZE_16BIT : 0;
188 	attrmem = (mem->kind & PCCARD_MEM_ATTR) ?
189 	    EXCA_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0;
190 	offset = ((mem->cardaddr >> EXCA_CARDMEM_ADDRX_SHIFT) -
191 	  (mem->addr >> EXCA_SYSMEM_ADDRX_SHIFT)) & 0x3fff;
192 	exca_putb(sc, map->sysmem_start_lsb,
193 	    mem->addr >> EXCA_SYSMEM_ADDRX_SHIFT);
194 	exca_putb(sc, map->sysmem_start_msb,
195 	    ((mem->addr >> (EXCA_SYSMEM_ADDRX_SHIFT + 8)) &
196 	    EXCA_SYSMEM_ADDRX_START_MSB_ADDR_MASK) | mem16);
197 
198 	exca_putb(sc, map->sysmem_stop_lsb,
199 	    (mem->addr + mem->realsize - 1) >> EXCA_SYSMEM_ADDRX_SHIFT);
200 	exca_putb(sc, map->sysmem_stop_msb,
201 	    (((mem->addr + mem->realsize - 1) >>
202 	    (EXCA_SYSMEM_ADDRX_SHIFT + 8)) &
203 	    EXCA_SYSMEM_ADDRX_STOP_MSB_ADDR_MASK) |
204 	    EXCA_SYSMEM_ADDRX_STOP_MSB_WAIT2);
205 	exca_putb(sc, map->sysmem_win, mem->addr >> EXCA_MEMREG_WIN_SHIFT);
206 
207 	exca_putb(sc, map->cardmem_lsb, offset & 0xff);
208 	exca_putb(sc, map->cardmem_msb, ((offset >> 8) &
209 	    EXCA_CARDMEM_ADDRX_MSB_ADDR_MASK) | attrmem);
210 
211 	DPRINTF("%s %d-bit memory",
212 	    mem->kind & PCCARD_MEM_ATTR ? "attribute" : "common",
213 	    mem->kind & PCCARD_MEM_16BIT ? 16 : 8);
214 	exca_setb(sc, EXCA_ADDRWIN_ENABLE, map->memenable |
215 	    EXCA_ADDRWIN_ENABLE_MEMCS16);
216 
217 	DELAY(100);
218 #ifdef EXCA_DEBUG
219 	{
220 		int r1, r2, r3, r4, r5, r6, r7;
221 		r1 = exca_getb(sc, map->sysmem_start_msb);
222 		r2 = exca_getb(sc, map->sysmem_start_lsb);
223 		r3 = exca_getb(sc, map->sysmem_stop_msb);
224 		r4 = exca_getb(sc, map->sysmem_stop_lsb);
225 		r5 = exca_getb(sc, map->cardmem_msb);
226 		r6 = exca_getb(sc, map->cardmem_lsb);
227 		r7 = exca_getb(sc, map->sysmem_win);
228 		printf("exca_do_mem_map win %d: %#02x%#02x %#02x%#02x "
229 		    "%#02x%#02x %#02x (%#08x+%#06x.%#06x*%#06x) flags %#x\n",
230 		    win, r1, r2, r3, r4, r5, r6, r7,
231 		    mem->addr, mem->size, mem->realsize,
232 		    mem->cardaddr, mem->kind);
233 	}
234 #endif
235 }
236 
237 /*
238  * public interface to map a resource.  kind is the type of memory to
239  * map (either common or attribute).  Memory created via this interface
240  * starts out at card address 0.  Since the only way to set this is
241  * to set it on a struct resource after it has been mapped, we're safe
242  * in mapping this assumption.  Note that resources can be remapped using
243  * exca_do_mem_map so that's how the card address can be set later.
244  */
245 int
246 exca_mem_map(struct exca_softc *sc, int kind, struct resource *res)
247 {
248 	int win;
249 
250 	for (win = 0; win < EXCA_MEM_WINS; win++) {
251 		if ((sc->memalloc & (1 << win)) == 0) {
252 			sc->memalloc |= (1 << win);
253 			break;
254 		}
255 	}
256 	if (win >= EXCA_MEM_WINS)
257 		return (ENOSPC);
258 	if (sc->flags & EXCA_HAS_MEMREG_WIN) {
259 #ifdef __LP64__
260 		if (rman_get_start(res) >> (EXCA_MEMREG_WIN_SHIFT + 8) != 0) {
261 			device_printf(sc->dev,
262 			    "Does not support mapping above 4GB.");
263 			return (EINVAL);
264 		}
265 #endif
266 	} else {
267 		if (rman_get_start(res) >> EXCA_MEMREG_WIN_SHIFT != 0) {
268 			device_printf(sc->dev,
269 			    "Does not support mapping above 16M.");
270 			return (EINVAL);
271 		}
272 	}
273 
274 	sc->mem[win].cardaddr = 0;
275 	sc->mem[win].memt = rman_get_bustag(res);
276 	sc->mem[win].memh = rman_get_bushandle(res);
277 	sc->mem[win].addr = rman_get_start(res);
278 	sc->mem[win].size = rman_get_end(res) - sc->mem[win].addr + 1;
279 	sc->mem[win].realsize = sc->mem[win].size + EXCA_MEM_PAGESIZE - 1;
280 	sc->mem[win].realsize = sc->mem[win].realsize -
281 	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
282 	sc->mem[win].kind = kind;
283 	DPRINTF("exca_mem_map window %d bus %x+%x card addr %x\n",
284 	    win, sc->mem[win].addr, sc->mem[win].size, sc->mem[win].cardaddr);
285 	exca_do_mem_map(sc, win);
286 
287 	return (0);
288 }
289 
290 /*
291  * Private helper function.  This turns off a given memory map that is in
292  * use.  We do this by just clearing the enable bit in the pcic.  If we needed
293  * to make memory unmapping/mapping pairs faster, we would have to store
294  * more state information about the pcic and then use that to intelligently
295  * to the map/unmap.  However, since we don't do that sort of thing often
296  * (generally just at configure time), it isn't a case worth optimizing.
297  */
298 static void
299 exca_mem_unmap(struct exca_softc *sc, int window)
300 {
301 	if (window < 0 || window >= EXCA_MEM_WINS)
302 		panic("exca_mem_unmap: window out of range");
303 
304 	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, mem_map_index[window].memenable);
305 	sc->memalloc &= ~(1 << window);
306 }
307 
308 /*
309  * Find the map that we're using to hold the resource.  This works well
310  * so long as the client drivers don't do silly things like map the same
311  * area mutliple times, or map both common and attribute memory at the
312  * same time.  This latter restriction is a bug.  We likely should just
313  * store a pointer to the res in the mem[x] data structure.
314  */
315 static int
316 exca_mem_findmap(struct exca_softc *sc, struct resource *res)
317 {
318 	int win;
319 
320 	for (win = 0; win < EXCA_MEM_WINS; win++) {
321 		if (sc->mem[win].memt == rman_get_bustag(res) &&
322 		    sc->mem[win].addr == rman_get_start(res) &&
323 		    sc->mem[win].size == rman_get_size(res))
324 			return (win);
325 	}
326 	return (-1);
327 }
328 
329 /*
330  * Set the memory flag.  This means that we are setting if the memory
331  * is coming from attribute memory or from common memory on the card.
332  * CIS entries are generally in attribute memory (although they can
333  * reside in common memory).  Generally, this is the only use for attribute
334  * memory.  However, some cards require their drivers to dance in both
335  * common and/or attribute memory and this interface (and setting the
336  * offset interface) exist for such cards.
337  */
338 int
339 exca_mem_set_flags(struct exca_softc *sc, struct resource *res, uint32_t flags)
340 {
341 	int win;
342 
343 	win = exca_mem_findmap(sc, res);
344 	if (win < 0) {
345 		device_printf(sc->dev,
346 		    "set_res_flags: specified resource not active\n");
347 		return (ENOENT);
348 	}
349 
350 	switch (flags)
351 	{
352 	case PCCARD_A_MEM_ATTR:
353 		sc->mem[win].kind |= PCCARD_MEM_ATTR;
354 		break;
355 	case PCCARD_A_MEM_COM:
356 		sc->mem[win].kind &= ~PCCARD_MEM_ATTR;
357 		break;
358 	case PCCARD_A_MEM_16BIT:
359 		sc->mem[win].kind |= PCCARD_MEM_16BIT;
360 		break;
361 	case PCCARD_A_MEM_8BIT:
362 		sc->mem[win].kind &= ~PCCARD_MEM_16BIT;
363 		break;
364 	}
365 	exca_do_mem_map(sc, win);
366 	return (0);
367 }
368 
369 /*
370  * Given a resource, go ahead and unmap it if we can find it in the
371  * resrouce list that's used.
372  */
373 int
374 exca_mem_unmap_res(struct exca_softc *sc, struct resource *res)
375 {
376 	int win;
377 
378 	win = exca_mem_findmap(sc, res);
379 	if (win < 0)
380 		return (ENOENT);
381 	exca_mem_unmap(sc, win);
382 	return (0);
383 }
384 
385 /*
386  * Set the offset of the memory.  We use this for reading the CIS and
387  * frobbing the pccard's pccard registers (CCR, etc).  Some drivers
388  * need to access arbitrary attribute and common memory during their
389  * initialization and operation.
390  */
391 int
392 exca_mem_set_offset(struct exca_softc *sc, struct resource *res,
393     uint32_t cardaddr, uint32_t *deltap)
394 {
395 	int win;
396 	uint32_t delta;
397 
398 	win = exca_mem_findmap(sc, res);
399 	if (win < 0) {
400 		device_printf(sc->dev,
401 		    "set_memory_offset: specified resource not active\n");
402 		return (ENOENT);
403 	}
404 	sc->mem[win].cardaddr = rounddown2(cardaddr, EXCA_MEM_PAGESIZE);
405 	delta = cardaddr % EXCA_MEM_PAGESIZE;
406 	if (deltap)
407 		*deltap = delta;
408 	sc->mem[win].realsize = sc->mem[win].size + delta +
409 	    EXCA_MEM_PAGESIZE - 1;
410 	sc->mem[win].realsize = sc->mem[win].realsize -
411 	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
412 	exca_do_mem_map(sc, win);
413 	return (0);
414 }
415 
416 
417 /* I/O */
418 
419 #define	EXCA_IOINFO(NUM) {						\
420 	EXCA_IOADDR ## NUM ## _START_LSB,				\
421 	EXCA_IOADDR ## NUM ## _START_MSB,				\
422 	EXCA_IOADDR ## NUM ## _STOP_LSB,				\
423 	EXCA_IOADDR ## NUM ## _STOP_MSB,				\
424 	EXCA_ADDRWIN_ENABLE_IO ## NUM,					\
425 	EXCA_IOCTL_IO ## NUM ## _WAITSTATE				\
426 	| EXCA_IOCTL_IO ## NUM ## _ZEROWAIT				\
427 	| EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_MASK			\
428 	| EXCA_IOCTL_IO ## NUM ## _DATASIZE_MASK,			\
429 	{								\
430 		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_CARD,		\
431 		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
432 		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_8BIT,		\
433 		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
434 		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_16BIT,		\
435 	}								\
436 }
437 
438 static struct io_map_index_st {
439 	int	start_lsb;
440 	int	start_msb;
441 	int	stop_lsb;
442 	int	stop_msb;
443 	int	ioenable;
444 	int	ioctlmask;
445 	int	ioctlbits[3]; /* indexed by PCCARD_WIDTH_* */
446 } io_map_index[] = {
447 	EXCA_IOINFO(0),
448 	EXCA_IOINFO(1),
449 };
450 #undef	EXCA_IOINFO
451 
452 static void
453 exca_do_io_map(struct exca_softc *sc, int win)
454 {
455 	struct io_map_index_st *map;
456 
457 	struct pccard_io_handle *io;
458 
459 	map = &io_map_index[win];
460 	io = &sc->io[win];
461 	exca_putb(sc, map->start_lsb, io->addr & 0xff);
462 	exca_putb(sc, map->start_msb, (io->addr >> 8) & 0xff);
463 
464 	exca_putb(sc, map->stop_lsb, (io->addr + io->size - 1) & 0xff);
465 	exca_putb(sc, map->stop_msb, ((io->addr + io->size - 1) >> 8) & 0xff);
466 
467 	exca_clrb(sc, EXCA_IOCTL, map->ioctlmask);
468 	exca_setb(sc, EXCA_IOCTL, map->ioctlbits[io->width]);
469 
470 	exca_setb(sc, EXCA_ADDRWIN_ENABLE, map->ioenable);
471 #ifdef EXCA_DEBUG
472 	{
473 		int r1, r2, r3, r4;
474 		r1 = exca_getb(sc, map->start_msb);
475 		r2 = exca_getb(sc, map->start_lsb);
476 		r3 = exca_getb(sc, map->stop_msb);
477 		r4 = exca_getb(sc, map->stop_lsb);
478 		DPRINTF("exca_do_io_map window %d: %02x%02x %02x%02x "
479 		    "(%08x+%08x)\n", win, r1, r2, r3, r4,
480 		    io->addr, io->size);
481 	}
482 #endif
483 }
484 
485 int
486 exca_io_map(struct exca_softc *sc, int width, struct resource *r)
487 {
488 	int win;
489 #ifdef EXCA_DEBUG
490 	static char *width_names[] = { "auto", "io8", "io16"};
491 #endif
492 	for (win=0; win < EXCA_IO_WINS; win++) {
493 		if ((sc->ioalloc & (1 << win)) == 0) {
494 			sc->ioalloc |= (1 << win);
495 			break;
496 		}
497 	}
498 	if (win >= EXCA_IO_WINS)
499 		return (ENOSPC);
500 
501 	sc->io[win].iot = rman_get_bustag(r);
502 	sc->io[win].ioh = rman_get_bushandle(r);
503 	sc->io[win].addr = rman_get_start(r);
504 	sc->io[win].size = rman_get_end(r) - sc->io[win].addr + 1;
505 	sc->io[win].flags = 0;
506 	sc->io[win].width = width;
507 	DPRINTF("exca_io_map window %d %s port %x+%x\n",
508 	    win, width_names[width], sc->io[win].addr,
509 	    sc->io[win].size);
510 	exca_do_io_map(sc, win);
511 
512 	return (0);
513 }
514 
515 static void
516 exca_io_unmap(struct exca_softc *sc, int window)
517 {
518 	if (window >= EXCA_IO_WINS)
519 		panic("exca_io_unmap: window out of range");
520 
521 	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, io_map_index[window].ioenable);
522 
523 	sc->ioalloc &= ~(1 << window);
524 
525 	sc->io[window].iot = 0;
526 	sc->io[window].ioh = 0;
527 	sc->io[window].addr = 0;
528 	sc->io[window].size = 0;
529 	sc->io[window].flags = 0;
530 	sc->io[window].width = 0;
531 }
532 
533 static int
534 exca_io_findmap(struct exca_softc *sc, struct resource *res)
535 {
536 	int win;
537 
538 	for (win = 0; win < EXCA_IO_WINS; win++) {
539 		if (sc->io[win].iot == rman_get_bustag(res) &&
540 		    sc->io[win].addr == rman_get_start(res) &&
541 		    sc->io[win].size == rman_get_size(res))
542 			return (win);
543 	}
544 	return (-1);
545 }
546 
547 
548 int
549 exca_io_unmap_res(struct exca_softc *sc, struct resource *res)
550 {
551 	int win;
552 
553 	win = exca_io_findmap(sc, res);
554 	if (win < 0)
555 		return (ENOENT);
556 	exca_io_unmap(sc, win);
557 	return (0);
558 }
559 
560 /* Misc */
561 
562 /*
563  * If interrupts are enabled, then we should be able to just wait for
564  * an interrupt routine to wake us up.  Busy waiting shouldn't be
565  * necessary.  Sadly, not all legacy ISA cards support an interrupt
566  * for the busy state transitions, at least according to their datasheets,
567  * so we busy wait a while here..
568  */
569 static void
570 exca_wait_ready(struct exca_softc *sc)
571 {
572 	int i;
573 	DEVPRINTF(sc->dev, "exca_wait_ready: status 0x%02x\n",
574 	    exca_getb(sc, EXCA_IF_STATUS));
575 	for (i = 0; i < 10000; i++) {
576 		if (exca_getb(sc, EXCA_IF_STATUS) & EXCA_IF_STATUS_READY)
577 			return;
578 		DELAY(500);
579 	}
580 	device_printf(sc->dev, "ready never happened, status = %02x\n",
581 	    exca_getb(sc, EXCA_IF_STATUS));
582 }
583 
584 /*
585  * Reset the card.  Ideally, we'd do a lot of this via interrupts.
586  * However, many PC Cards will deassert the ready signal.  This means
587  * that they are asserting an interrupt.  This makes it hard to
588  * do anything but a busy wait here.  One could argue that these
589  * such cards are broken, or that the bridge that allows this sort
590  * of interrupt through isn't quite what you'd want (and may be a standards
591  * violation).  However, such arguing would leave a huge class of PC Cards
592  * and bridges out of reach for use in the system.
593  *
594  * Maybe I should reevaluate the above based on the power bug I fixed
595  * in OLDCARD.
596  */
597 void
598 exca_reset(struct exca_softc *sc, device_t child)
599 {
600 	int win;
601 
602 	/* enable socket i/o */
603 	exca_setb(sc, EXCA_PWRCTL, EXCA_PWRCTL_OE);
604 
605 	exca_putb(sc, EXCA_INTR, EXCA_INTR_ENABLE);
606 	/* hold reset for 30ms */
607 	DELAY(30*1000);
608 	/* clear the reset flag */
609 	exca_setb(sc, EXCA_INTR, EXCA_INTR_RESET);
610 	/* wait 20ms as per PC Card standard (r2.01) section 4.3.6 */
611 	DELAY(20*1000);
612 
613 	exca_wait_ready(sc);
614 
615 	/* disable all address windows */
616 	exca_putb(sc, EXCA_ADDRWIN_ENABLE, 0);
617 
618 	exca_setb(sc, EXCA_INTR, EXCA_INTR_CARDTYPE_IO);
619 	DEVPRINTF(sc->dev, "card type is io\n");
620 
621 	/* reinstall all the memory and io mappings */
622 	for (win = 0; win < EXCA_MEM_WINS; ++win)
623 		if (sc->memalloc & (1 << win))
624 			exca_do_mem_map(sc, win);
625 	for (win = 0; win < EXCA_IO_WINS; ++win)
626 		if (sc->ioalloc & (1 << win))
627 			exca_do_io_map(sc, win);
628 }
629 
630 /*
631  * Initialize the exca_softc data structure for the first time.
632  */
633 void
634 exca_init(struct exca_softc *sc, device_t dev,
635     bus_space_tag_t bst, bus_space_handle_t bsh, uint32_t offset)
636 {
637 	sc->dev = dev;
638 	sc->memalloc = 0;
639 	sc->ioalloc = 0;
640 	sc->bst = bst;
641 	sc->bsh = bsh;
642 	sc->offset = offset;
643 	sc->flags = 0;
644 	sc->getb = exca_mem_getb;
645 	sc->putb = exca_mem_putb;
646 	sc->pccarddev = device_add_child(dev, "pccard", -1);
647 	if (sc->pccarddev == NULL)
648 		DEVPRINTF(brdev, "WARNING: cannot add pccard bus.\n");
649 	else if (device_probe_and_attach(sc->pccarddev) != 0)
650 		DEVPRINTF(brdev, "WARNING: cannot attach pccard bus.\n");
651 }
652 
653 /*
654  * Is this socket valid?
655  */
656 static int
657 exca_valid_slot(struct exca_softc *exca)
658 {
659 	uint8_t c;
660 
661 	/* Assume the worst */
662 	exca->chipset = EXCA_BOGUS;
663 
664 	/*
665 	 * see if there's a PCMCIA controller here
666 	 * Intel PCMCIA controllers use 0x82 and 0x83
667 	 * IBM clone chips use 0x88 and 0x89, apparently
668 	 */
669 	c = exca_getb(exca, EXCA_IDENT);
670 	DEVPRINTF(exca->dev, "Ident is %x\n", c);
671 	if ((c & EXCA_IDENT_IFTYPE_MASK) != EXCA_IDENT_IFTYPE_MEM_AND_IO)
672 		return (0);
673 	if ((c & EXCA_IDENT_ZERO) != 0)
674 		return (0);
675 	switch (c & EXCA_IDENT_REV_MASK) {
676 	/*
677 	 *	82365 or clones.
678 	 */
679 	case EXCA_IDENT_REV_I82365SLR0:
680 	case EXCA_IDENT_REV_I82365SLR1:
681 		exca->chipset = EXCA_I82365;
682 		/*
683 		 * Check for Vadem chips by unlocking their extra
684 		 * registers and looking for valid ID.  Bit 3 in
685 		 * the ID register is normally 0, except when
686 		 * EXCA_VADEMREV is set.  Other bridges appear
687 		 * to ignore this frobbing.
688 		 */
689 		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
690 		    EXCA_VADEM_COOKIE1);
691 		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
692 		    EXCA_VADEM_COOKIE2);
693 		exca_setb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
694 		c = exca_getb(exca, EXCA_IDENT);
695 		if (c & 0x08) {
696 			switch (c & 7) {
697 			case 1:
698 				exca->chipset = EXCA_VG365;
699 				break;
700 			case 2:
701 				exca->chipset = EXCA_VG465;
702 				break;
703 			case 3:
704 				exca->chipset = EXCA_VG468;
705 				break;
706 			default:
707 				exca->chipset = EXCA_VG469;
708 				break;
709 			}
710 			exca_clrb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
711 			break;
712 		}
713 		/*
714 		 * Check for RICOH RF5C[23]96 PCMCIA Controller
715 		 */
716 		c = exca_getb(exca, EXCA_RICOH_ID);
717 		if (c == EXCA_RID_396) {
718 			exca->chipset = EXCA_RF5C396;
719 			break;
720 		} else if (c == EXCA_RID_296) {
721 			exca->chipset = EXCA_RF5C296;
722 			break;
723 		}
724 		/*
725 		 *	Check for Cirrus logic chips.
726 		 */
727 		exca_putb(exca, EXCA_CIRRUS_CHIP_INFO, 0);
728 		c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
729 		if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) ==
730 		    EXCA_CIRRUS_CHIP_INFO_CHIP_ID) {
731 			c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
732 			if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) == 0) {
733 				if (c & EXCA_CIRRUS_CHIP_INFO_SLOTS)
734 					exca->chipset = EXCA_PD6722;
735 				else
736 					exca->chipset = EXCA_PD6710;
737 				break;
738 			}
739 		}
740 		break;
741 
742 	case EXCA_IDENT_REV_I82365SLDF:
743 		/*
744 		 *	Intel i82365sl-DF step or maybe a vlsi 82c146
745 		 * we detected the vlsi case earlier, so if the controller
746 		 * isn't set, we know it is a i82365sl step D.
747 		 * XXXX Except we didn't -- this is a regression but VLSI
748 		 * controllers are super hard to find these days for testing.
749 		 */
750 		exca->chipset = EXCA_I82365SL_DF;
751 		break;
752 	case EXCA_IDENT_REV_IBM1:
753 	case EXCA_IDENT_REV_IBM2:
754 		exca->chipset = EXCA_IBM;
755 		break;
756 	case EXCA_IDENT_REV_IBM_KING:
757 		exca->chipset = EXCA_IBM_KING;
758 		break;
759 	default:
760 		return (0);
761 	}
762 	return (1);
763 }
764 
765 /*
766  * Probe the expected slots.  We maybe should set the ID for each of these
767  * slots too while we're at it.  But maybe that belongs to a separate
768  * function.
769  *
770  * The caller must guarantee that at least EXCA_NSLOTS are present in exca.
771  */
772 int
773 exca_probe_slots(device_t dev, struct exca_softc *exca, bus_space_tag_t iot,
774     bus_space_handle_t ioh)
775 {
776 	int err;
777 	int i;
778 
779 	err = ENXIO;
780 	for (i = 0; i < EXCA_NSLOTS; i++)  {
781 		exca_init(&exca[i], dev, iot, ioh, i * EXCA_SOCKET_SIZE);
782 		exca->getb = exca_io_getb;
783 		exca->putb = exca_io_putb;
784 		if (exca_valid_slot(&exca[i])) {
785 			device_set_desc(dev, chip_names[exca[i].chipset]);
786 			err = 0;
787 		}
788 	}
789 	return (err);
790 }
791 
792 void
793 exca_insert(struct exca_softc *exca)
794 {
795 	if (device_is_attached(exca->pccarddev)) {
796 		if (CARD_ATTACH_CARD(exca->pccarddev) != 0)
797 			device_printf(exca->dev,
798 			    "PC Card card activation failed\n");
799 	} else {
800 		device_printf(exca->dev,
801 		    "PC Card inserted, but no pccard bus.\n");
802 	}
803 }
804 
805 
806 void
807 exca_removal(struct exca_softc *exca)
808 {
809 	if (device_is_attached(exca->pccarddev))
810 		CARD_DETACH_CARD(exca->pccarddev);
811 }
812 
813 int
814 exca_activate_resource(struct exca_softc *exca, device_t child, int type,
815     int rid, struct resource *res)
816 {
817 	int err;
818 
819 	if (rman_get_flags(res) & RF_ACTIVE)
820 		return (0);
821 	err = BUS_ACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
822 	    type, rid, res);
823 	if (err)
824 		return (err);
825 	switch (type) {
826 	case SYS_RES_IOPORT:
827 		err = exca_io_map(exca, PCCARD_WIDTH_AUTO, res);
828 		break;
829 	case SYS_RES_MEMORY:
830 		err = exca_mem_map(exca, 0, res);
831 		break;
832 	}
833 	if (err)
834 		BUS_DEACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
835 		    type, rid, res);
836 	return (err);
837 }
838 
839 int
840 exca_deactivate_resource(struct exca_softc *exca, device_t child, int type,
841     int rid, struct resource *res)
842 {
843 	if (rman_get_flags(res) & RF_ACTIVE) { /* if activated */
844 		switch (type) {
845 		case SYS_RES_IOPORT:
846 			if (exca_io_unmap_res(exca, res))
847 				return (ENOENT);
848 			break;
849 		case SYS_RES_MEMORY:
850 			if (exca_mem_unmap_res(exca, res))
851 				return (ENOENT);
852 			break;
853 		}
854 	}
855 	return (BUS_DEACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
856 	    type, rid, res));
857 }
858 
859 #if 0
860 static struct resource *
861 exca_alloc_resource(struct exca_softc *sc, device_t child, int type, int *rid,
862     u_long start, u_long end, u_long count, uint flags)
863 {
864 	struct resource *res = NULL;
865 	int tmp;
866 
867 	switch (type) {
868 	case SYS_RES_MEMORY:
869 		if (start < cbb_start_mem)
870 			start = cbb_start_mem;
871 		if (end < start)
872 			end = start;
873 		flags = (flags & ~RF_ALIGNMENT_MASK) |
874 		    rman_make_alignment_flags(CBB_MEMALIGN);
875 		break;
876 	case SYS_RES_IOPORT:
877 		if (start < cbb_start_16_io)
878 			start = cbb_start_16_io;
879 		if (end < start)
880 			end = start;
881 		break;
882 	case SYS_RES_IRQ:
883 		tmp = rman_get_start(sc->irq_res);
884 		if (start > tmp || end < tmp || count != 1) {
885 			device_printf(child, "requested interrupt %ld-%ld,"
886 			    "count = %ld not supported by cbb\n",
887 			    start, end, count);
888 			return (NULL);
889 		}
890 		flags |= RF_SHAREABLE;
891 		start = end = rman_get_start(sc->irq_res);
892 		break;
893 	}
894 	res = BUS_ALLOC_RESOURCE(up, child, type, rid,
895 	    start, end, count, flags & ~RF_ACTIVE);
896 	if (res == NULL)
897 		return (NULL);
898 	cbb_insert_res(sc, res, type, *rid);
899 	if (flags & RF_ACTIVE) {
900 		if (bus_activate_resource(child, type, *rid, res) != 0) {
901 			bus_release_resource(child, type, *rid, res);
902 			return (NULL);
903 		}
904 	}
905 
906 	return (res);
907 }
908 
909 static int
910 exca_release_resource(struct exca_softc *sc, device_t child, int type,
911     int rid, struct resource *res)
912 {
913 	int error;
914 
915 	if (rman_get_flags(res) & RF_ACTIVE) {
916 		error = bus_deactivate_resource(child, type, rid, res);
917 		if (error != 0)
918 			return (error);
919 	}
920 	cbb_remove_res(sc, res);
921 	return (BUS_RELEASE_RESOURCE(device_get_parent(brdev), child,
922 	    type, rid, res));
923 }
924 #endif
925 
926 static int
927 exca_modevent(module_t mod, int cmd, void *arg)
928 {
929 	return 0;
930 }
931 
932 DEV_MODULE(exca, exca_modevent, NULL);
933 MODULE_VERSION(exca, 1);
934