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