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