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