xref: /freebsd/sys/dev/exca/exca.c (revision d0b2dbfa)
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/cdefs.h>
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 mapping 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 = rounddown2(cardaddr, EXCA_MEM_PAGESIZE);
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 	sc->pccarddev = device_add_child(dev, "pccard", -1);
648 	if (sc->pccarddev == NULL)
649 		DEVPRINTF(brdev, "WARNING: cannot add pccard bus.\n");
650 	else if (device_probe_and_attach(sc->pccarddev) != 0)
651 		DEVPRINTF(brdev, "WARNING: cannot attach pccard bus.\n");
652 }
653 
654 /*
655  * Is this socket valid?
656  */
657 static int
658 exca_valid_slot(struct exca_softc *exca)
659 {
660 	uint8_t c;
661 
662 	/* Assume the worst */
663 	exca->chipset = EXCA_BOGUS;
664 
665 	/*
666 	 * see if there's a PCMCIA controller here
667 	 * Intel PCMCIA controllers use 0x82 and 0x83
668 	 * IBM clone chips use 0x88 and 0x89, apparently
669 	 */
670 	c = exca_getb(exca, EXCA_IDENT);
671 	DEVPRINTF(exca->dev, "Ident is %x\n", c);
672 	if ((c & EXCA_IDENT_IFTYPE_MASK) != EXCA_IDENT_IFTYPE_MEM_AND_IO)
673 		return (0);
674 	if ((c & EXCA_IDENT_ZERO) != 0)
675 		return (0);
676 	switch (c & EXCA_IDENT_REV_MASK) {
677 	/*
678 	 *	82365 or clones.
679 	 */
680 	case EXCA_IDENT_REV_I82365SLR0:
681 	case EXCA_IDENT_REV_I82365SLR1:
682 		exca->chipset = EXCA_I82365;
683 		/*
684 		 * Check for Vadem chips by unlocking their extra
685 		 * registers and looking for valid ID.  Bit 3 in
686 		 * the ID register is normally 0, except when
687 		 * EXCA_VADEMREV is set.  Other bridges appear
688 		 * to ignore this frobbing.
689 		 */
690 		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
691 		    EXCA_VADEM_COOKIE1);
692 		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
693 		    EXCA_VADEM_COOKIE2);
694 		exca_setb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
695 		c = exca_getb(exca, EXCA_IDENT);
696 		if (c & 0x08) {
697 			switch (c & 7) {
698 			case 1:
699 				exca->chipset = EXCA_VG365;
700 				break;
701 			case 2:
702 				exca->chipset = EXCA_VG465;
703 				break;
704 			case 3:
705 				exca->chipset = EXCA_VG468;
706 				break;
707 			default:
708 				exca->chipset = EXCA_VG469;
709 				break;
710 			}
711 			exca_clrb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
712 			break;
713 		}
714 		/*
715 		 * Check for RICOH RF5C[23]96 PCMCIA Controller
716 		 */
717 		c = exca_getb(exca, EXCA_RICOH_ID);
718 		if (c == EXCA_RID_396) {
719 			exca->chipset = EXCA_RF5C396;
720 			break;
721 		} else if (c == EXCA_RID_296) {
722 			exca->chipset = EXCA_RF5C296;
723 			break;
724 		}
725 		/*
726 		 *	Check for Cirrus logic chips.
727 		 */
728 		exca_putb(exca, EXCA_CIRRUS_CHIP_INFO, 0);
729 		c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
730 		if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) ==
731 		    EXCA_CIRRUS_CHIP_INFO_CHIP_ID) {
732 			c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
733 			if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) == 0) {
734 				if (c & EXCA_CIRRUS_CHIP_INFO_SLOTS)
735 					exca->chipset = EXCA_PD6722;
736 				else
737 					exca->chipset = EXCA_PD6710;
738 				break;
739 			}
740 		}
741 		break;
742 
743 	case EXCA_IDENT_REV_I82365SLDF:
744 		/*
745 		 *	Intel i82365sl-DF step or maybe a vlsi 82c146
746 		 * we detected the vlsi case earlier, so if the controller
747 		 * isn't set, we know it is a i82365sl step D.
748 		 * XXXX Except we didn't -- this is a regression but VLSI
749 		 * controllers are super hard to find these days for testing.
750 		 */
751 		exca->chipset = EXCA_I82365SL_DF;
752 		break;
753 	case EXCA_IDENT_REV_IBM1:
754 	case EXCA_IDENT_REV_IBM2:
755 		exca->chipset = EXCA_IBM;
756 		break;
757 	case EXCA_IDENT_REV_IBM_KING:
758 		exca->chipset = EXCA_IBM_KING;
759 		break;
760 	default:
761 		return (0);
762 	}
763 	return (1);
764 }
765 
766 /*
767  * Probe the expected slots.  We maybe should set the ID for each of these
768  * slots too while we're at it.  But maybe that belongs to a separate
769  * function.
770  *
771  * The caller must guarantee that at least EXCA_NSLOTS are present in exca.
772  */
773 int
774 exca_probe_slots(device_t dev, struct exca_softc *exca, bus_space_tag_t iot,
775     bus_space_handle_t ioh)
776 {
777 	int err;
778 	int i;
779 
780 	err = ENXIO;
781 	for (i = 0; i < EXCA_NSLOTS; i++)  {
782 		exca_init(&exca[i], dev, iot, ioh, i * EXCA_SOCKET_SIZE);
783 		exca->getb = exca_io_getb;
784 		exca->putb = exca_io_putb;
785 		if (exca_valid_slot(&exca[i])) {
786 			device_set_desc(dev, chip_names[exca[i].chipset]);
787 			err = 0;
788 		}
789 	}
790 	return (err);
791 }
792 
793 void
794 exca_insert(struct exca_softc *exca)
795 {
796 	if (device_is_attached(exca->pccarddev)) {
797 		if (CARD_ATTACH_CARD(exca->pccarddev) != 0)
798 			device_printf(exca->dev,
799 			    "PC Card card activation failed\n");
800 	} else {
801 		device_printf(exca->dev,
802 		    "PC Card inserted, but no pccard bus.\n");
803 	}
804 }
805 
806 
807 void
808 exca_removal(struct exca_softc *exca)
809 {
810 	if (device_is_attached(exca->pccarddev))
811 		CARD_DETACH_CARD(exca->pccarddev);
812 }
813 
814 int
815 exca_activate_resource(struct exca_softc *exca, device_t child, int type,
816     int rid, struct resource *res)
817 {
818 	int err;
819 
820 	if (rman_get_flags(res) & RF_ACTIVE)
821 		return (0);
822 	err = BUS_ACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
823 	    type, rid, res);
824 	if (err)
825 		return (err);
826 	switch (type) {
827 	case SYS_RES_IOPORT:
828 		err = exca_io_map(exca, PCCARD_WIDTH_AUTO, res);
829 		break;
830 	case SYS_RES_MEMORY:
831 		err = exca_mem_map(exca, 0, res);
832 		break;
833 	}
834 	if (err)
835 		BUS_DEACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
836 		    type, rid, res);
837 	return (err);
838 }
839 
840 int
841 exca_deactivate_resource(struct exca_softc *exca, device_t child, int type,
842     int rid, struct resource *res)
843 {
844 	if (rman_get_flags(res) & RF_ACTIVE) { /* if activated */
845 		switch (type) {
846 		case SYS_RES_IOPORT:
847 			if (exca_io_unmap_res(exca, res))
848 				return (ENOENT);
849 			break;
850 		case SYS_RES_MEMORY:
851 			if (exca_mem_unmap_res(exca, res))
852 				return (ENOENT);
853 			break;
854 		}
855 	}
856 	return (BUS_DEACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
857 	    type, rid, res));
858 }
859 
860 #if 0
861 static struct resource *
862 exca_alloc_resource(struct exca_softc *sc, device_t child, int type, int *rid,
863     u_long start, u_long end, u_long count, uint flags)
864 {
865 	struct resource *res = NULL;
866 	int tmp;
867 
868 	switch (type) {
869 	case SYS_RES_MEMORY:
870 		if (start < cbb_start_mem)
871 			start = cbb_start_mem;
872 		if (end < start)
873 			end = start;
874 		flags = (flags & ~RF_ALIGNMENT_MASK) |
875 		    rman_make_alignment_flags(CBB_MEMALIGN);
876 		break;
877 	case SYS_RES_IOPORT:
878 		if (start < cbb_start_16_io)
879 			start = cbb_start_16_io;
880 		if (end < start)
881 			end = start;
882 		break;
883 	case SYS_RES_IRQ:
884 		tmp = rman_get_start(sc->irq_res);
885 		if (start > tmp || end < tmp || count != 1) {
886 			device_printf(child, "requested interrupt %ld-%ld,"
887 			    "count = %ld not supported by cbb\n",
888 			    start, end, count);
889 			return (NULL);
890 		}
891 		flags |= RF_SHAREABLE;
892 		start = end = rman_get_start(sc->irq_res);
893 		break;
894 	}
895 	res = BUS_ALLOC_RESOURCE(up, child, type, rid,
896 	    start, end, count, flags & ~RF_ACTIVE);
897 	if (res == NULL)
898 		return (NULL);
899 	cbb_insert_res(sc, res, type, *rid);
900 	if (flags & RF_ACTIVE) {
901 		if (bus_activate_resource(child, type, *rid, res) != 0) {
902 			bus_release_resource(child, type, *rid, res);
903 			return (NULL);
904 		}
905 	}
906 
907 	return (res);
908 }
909 
910 static int
911 exca_release_resource(struct exca_softc *sc, device_t child, int type,
912     int rid, struct resource *res)
913 {
914 	int error;
915 
916 	if (rman_get_flags(res) & RF_ACTIVE) {
917 		error = bus_deactivate_resource(child, type, rid, res);
918 		if (error != 0)
919 			return (error);
920 	}
921 	cbb_remove_res(sc, res);
922 	return (BUS_RELEASE_RESOURCE(device_get_parent(brdev), child,
923 	    type, rid, res));
924 }
925 #endif
926 
927 static int
928 exca_modevent(module_t mod, int cmd, void *arg)
929 {
930 	return 0;
931 }
932 
933 DEV_MODULE(exca, exca_modevent, NULL);
934 MODULE_VERSION(exca, 1);
935