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