exca.c revision 151309
1139749Simp/*-
2140022Simp * Copyright (c) 2002-2005 M Warner Losh.  All rights reserved.
389948Simp *
489948Simp * Redistribution and use in source and binary forms, with or without
589948Simp * modification, are permitted provided that the following conditions
689948Simp * are met:
789948Simp * 1. Redistributions of source code must retain the above copyright
889948Simp *    notice, this list of conditions and the following disclaimer.
989948Simp * 2. Redistributions in binary form must reproduce the above copyright
1089948Simp *    notice, this list of conditions and the following disclaimer in the
1189948Simp *    documentation and/or other materials provided with the distribution.
1289948Simp *
1389948Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1489948Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1589948Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1689948Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1789948Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1889948Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1989948Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2089948Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2189948Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2289948Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2389948Simp *
2489948Simp * This software may be derived from NetBSD i82365.c and other files with
2589948Simp * the following copyright:
2689948Simp *
2789948Simp * Copyright (c) 1997 Marc Horowitz.  All rights reserved.
2889948Simp *
2989948Simp * Redistribution and use in source and binary forms, with or without
3089948Simp * modification, are permitted provided that the following conditions
3189948Simp * are met:
3289948Simp * 1. Redistributions of source code must retain the above copyright
3389948Simp *    notice, this list of conditions and the following disclaimer.
3489948Simp * 2. Redistributions in binary form must reproduce the above copyright
3589948Simp *    notice, this list of conditions and the following disclaimer in the
3689948Simp *    documentation and/or other materials provided with the distribution.
3789948Simp * 3. All advertising materials mentioning features or use of this software
3889948Simp *    must display the following acknowledgement:
3989948Simp *	This product includes software developed by Marc Horowitz.
4089948Simp * 4. The name of the author may not be used to endorse or promote products
4189948Simp *    derived from this software without specific prior written permission.
4289948Simp *
4389948Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
4489948Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
4589948Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
4689948Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
4789948Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
4889948Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
4989948Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
5089948Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
5189948Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
5289948Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5389948Simp */
5489948Simp
55119511Simp#include <sys/cdefs.h>
56119511Simp__FBSDID("$FreeBSD: head/sys/dev/exca/exca.c 151309 2005-10-14 04:48:11Z imp $");
57119511Simp
5889948Simp#include <sys/param.h>
5989948Simp#include <sys/systm.h>
60115988Simp#include <sys/condvar.h>
6189948Simp#include <sys/errno.h>
6289948Simp#include <sys/kernel.h>
6389948Simp#include <sys/malloc.h>
6489948Simp#include <sys/queue.h>
6597613Stakawata#include <sys/module.h>
66115988Simp#include <sys/lock.h>
67115988Simp#include <sys/mutex.h>
6897613Stakawata#include <sys/conf.h>
6989948Simp
7089948Simp#include <sys/bus.h>
7189948Simp#include <machine/bus.h>
7289948Simp#include <sys/rman.h>
7389948Simp#include <machine/resource.h>
7489948Simp
7589948Simp#include <dev/pccard/pccardreg.h>
7689948Simp#include <dev/pccard/pccardvar.h>
7789948Simp
7889948Simp#include <dev/exca/excareg.h>
7989948Simp#include <dev/exca/excavar.h>
8089948Simp
8189948Simp#ifdef EXCA_DEBUG
8289948Simp#define DEVPRINTF(dev, fmt, args...)	device_printf((dev), (fmt), ## args)
8389948Simp#define DPRINTF(fmt, args...)		printf(fmt, ## args)
8489948Simp#else
8589948Simp#define DEVPRINTF(dev, fmt, args...)
8689948Simp#define DPRINTF(fmt, args...)
8789948Simp#endif
8889948Simp
89110841Simp#if 0
90110841Simpstatic const char *chip_names[] =
91110841Simp{
92110841Simp	"CardBus socket",
93110841Simp	"Intel i82365SL-A/B or clone",
94110841Simp	"Intel i82365sl-DF step",
95110841Simp	"VLSI chip",
96110841Simp	"Cirrus Logic PD6710",
97110841Simp	"Cirrus logic PD6722",
98110841Simp	"Cirrus Logic PD6729",
99110841Simp	"Vadem 365",
100110841Simp	"Vadem 465",
101110841Simp	"Vadem 468",
102110841Simp	"Vadem 469",
103110841Simp	"Ricoh RF5C296",
104110841Simp	"Ricoh RF5C396",
105110841Simp	"IBM clone",
106110841Simp	"IBM KING PCMCIA Controller"
107110841Simp};
108110841Simp#endif
109110841Simp
110110841Simpstatic exca_getb_fn exca_mem_getb;
111110841Simpstatic exca_putb_fn exca_mem_putb;
112110841Simpstatic exca_getb_fn exca_io_getb;
113110841Simpstatic exca_putb_fn exca_io_putb;
114110841Simp
11589948Simp/* memory */
11689948Simp
11789948Simp#define	EXCA_MEMINFO(NUM) {						\
11889948Simp	EXCA_SYSMEM_ADDR ## NUM ## _START_LSB,				\
11989948Simp	EXCA_SYSMEM_ADDR ## NUM ## _START_MSB,				\
12089948Simp	EXCA_SYSMEM_ADDR ## NUM ## _STOP_LSB,				\
12189948Simp	EXCA_SYSMEM_ADDR ## NUM ## _STOP_MSB,				\
12289948Simp	EXCA_SYSMEM_ADDR ## NUM ## _WIN,				\
12389948Simp	EXCA_CARDMEM_ADDR ## NUM ## _LSB,				\
12489948Simp	EXCA_CARDMEM_ADDR ## NUM ## _MSB,				\
12597708Salfred	EXCA_ADDRWIN_ENABLE_MEM ## NUM,					\
12689948Simp}
12789948Simp
12889948Simpstatic struct mem_map_index_st {
12989948Simp	int	sysmem_start_lsb;
13089948Simp	int	sysmem_start_msb;
13189948Simp	int	sysmem_stop_lsb;
13289948Simp	int	sysmem_stop_msb;
13389948Simp	int	sysmem_win;
13489948Simp	int	cardmem_lsb;
13589948Simp	int	cardmem_msb;
13689948Simp	int	memenable;
13789948Simp} mem_map_index[] = {
13889948Simp	EXCA_MEMINFO(0),
13989948Simp	EXCA_MEMINFO(1),
14089948Simp	EXCA_MEMINFO(2),
14189948Simp	EXCA_MEMINFO(3),
14289948Simp	EXCA_MEMINFO(4)
14389948Simp};
14489948Simp#undef	EXCA_MEMINFO
14589948Simp
146110841Simpstatic uint8_t
147110841Simpexca_mem_getb(struct exca_softc *sc, int reg)
148110841Simp{
149110841Simp	return (bus_space_read_1(sc->bst, sc->bsh, sc->offset + reg));
150110841Simp}
151110841Simp
152110841Simpstatic void
153110841Simpexca_mem_putb(struct exca_softc *sc, int reg, uint8_t val)
154110841Simp{
155115461Sphk	bus_space_write_1(sc->bst, sc->bsh, sc->offset + reg, val);
156110841Simp}
157110841Simp
158110841Simpstatic uint8_t
159110841Simpexca_io_getb(struct exca_softc *sc, int reg)
160110841Simp{
161110841Simp	bus_space_write_1(sc->bst, sc->bsh, EXCA_REG_INDEX, reg + sc->offset);
162110841Simp	return (bus_space_read_1(sc->bst, sc->bsh, EXCA_REG_DATA));
163110841Simp}
164110841Simp
165110841Simpstatic void
166110841Simpexca_io_putb(struct exca_softc *sc, int reg, uint8_t val)
167110841Simp{
168110841Simp	bus_space_write_1(sc->bst, sc->bsh, EXCA_REG_INDEX, reg + sc->offset);
169110841Simp	bus_space_write_1(sc->bst, sc->bsh, EXCA_REG_DATA, val);
170110841Simp}
171110841Simp
17289948Simp/*
17389948Simp * Helper function.  This will map the requested memory slot.  We setup the
17489948Simp * map before we call this function.  This is used to initially force the
17589948Simp * mapping, as well as later restore the mapping after it has been destroyed
17689948Simp * in some fashion (due to a power event typically).
17789948Simp */
17889948Simpstatic void
17989948Simpexca_do_mem_map(struct exca_softc *sc, int win)
18089948Simp{
18189948Simp	struct mem_map_index_st *map;
18289948Simp	struct pccard_mem_handle *mem;
183140022Simp	uint32_t offset;
184151309Simp	int mem8 = (mem->kind == PCCARD_A_MEM_ATTR);
185151309Simp	mem8 = 1;
18689948Simp
18789948Simp	map = &mem_map_index[win];
18889948Simp	mem = &sc->mem[win];
189140022Simp	offset = ((mem->cardaddr >> EXCA_CARDMEM_ADDRX_SHIFT) -
190140022Simp	  (mem->addr >> EXCA_SYSMEM_ADDRX_SHIFT)) & 0x3fff;
191110841Simp	exca_putb(sc, map->sysmem_start_lsb,
19289948Simp	    (mem->addr >> EXCA_SYSMEM_ADDRX_SHIFT) & 0xff);
193110841Simp	exca_putb(sc, map->sysmem_start_msb,
19489948Simp	    ((mem->addr >> (EXCA_SYSMEM_ADDRX_SHIFT + 8)) &
195151309Simp	    EXCA_SYSMEM_ADDRX_START_MSB_ADDR_MASK) |
196151309Simp	    (mem8 ? 0 : EXCA_SYSMEM_ADDRX_START_MSB_DATASIZE_16BIT));
19789948Simp
198110841Simp	exca_putb(sc, map->sysmem_stop_lsb,
19989948Simp	    ((mem->addr + mem->realsize - 1) >>
20089948Simp	    EXCA_SYSMEM_ADDRX_SHIFT) & 0xff);
201110841Simp	exca_putb(sc, map->sysmem_stop_msb,
20289948Simp	    (((mem->addr + mem->realsize - 1) >>
20389948Simp	    (EXCA_SYSMEM_ADDRX_SHIFT + 8)) &
204133782Simp	    EXCA_SYSMEM_ADDRX_STOP_MSB_ADDR_MASK) |
205133782Simp	    EXCA_SYSMEM_ADDRX_STOP_MSB_WAIT2);
20689948Simp
207110841Simp	exca_putb(sc, map->sysmem_win,
20889948Simp	    (mem->addr >> EXCA_MEMREG_WIN_SHIFT) & 0xff);
20989948Simp
210140022Simp	exca_putb(sc, map->cardmem_lsb, offset & 0xff);
211140022Simp	exca_putb(sc, map->cardmem_msb, (((offset >> 8) & 0xff) &
21289948Simp	    EXCA_CARDMEM_ADDRX_MSB_ADDR_MASK) |
213119551Simp	    ((mem->kind == PCCARD_A_MEM_ATTR) ?
21489948Simp	    EXCA_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0));
21589948Simp
216119520Simp#ifdef EXCA_DEBUG
217119551Simp	if (mem->kind == PCCARD_A_MEM_ATTR)
218119520Simp		printf("attribtue memory\n");
219119520Simp	else
220119520Simp		printf("common memory\n");
221119520Simp#endif
222133782Simp	exca_setb(sc, EXCA_ADDRWIN_ENABLE, map->memenable |
223133782Simp	    EXCA_ADDRWIN_ENABLE_MEMCS16);
22489948Simp
22589948Simp	DELAY(100);
22689948Simp#ifdef EXCA_DEBUG
22789948Simp	{
22889948Simp		int r1, r2, r3, r4, r5, r6, r7;
229110841Simp		r1 = exca_getb(sc, map->sysmem_start_msb);
230110841Simp		r2 = exca_getb(sc, map->sysmem_start_lsb);
231110841Simp		r3 = exca_getb(sc, map->sysmem_stop_msb);
232110841Simp		r4 = exca_getb(sc, map->sysmem_stop_lsb);
233110841Simp		r5 = exca_getb(sc, map->cardmem_msb);
234110841Simp		r6 = exca_getb(sc, map->cardmem_lsb);
235110841Simp		r7 = exca_getb(sc, map->sysmem_win);
236133782Simp		printf("exca_do_mem_map win %d: %02x%02x %02x%02x "
237133782Simp		    "%02x%02x %02x (%08x+%06x.%06x*%06x)\n",
23889948Simp		    win, r1, r2, r3, r4, r5, r6, r7,
23989948Simp		    mem->addr, mem->size, mem->realsize,
240119520Simp		    mem->cardaddr);
24189948Simp	}
24289948Simp#endif
24389948Simp}
24489948Simp
24589948Simp/*
24689948Simp * public interface to map a resource.  kind is the type of memory to
24789948Simp * map (either common or attribute).  Memory created via this interface
24889948Simp * starts out at card address 0.  Since the only way to set this is
24989948Simp * to set it on a struct resource after it has been mapped, we're safe
25089948Simp * in maping this assumption.  Note that resources can be remapped using
25189948Simp * exca_do_mem_map so that's how the card address can be set later.
25289948Simp */
25389948Simpint
25489948Simpexca_mem_map(struct exca_softc *sc, int kind, struct resource *res)
25589948Simp{
25689948Simp	int win;
25789948Simp
25889948Simp	for (win = 0; win < EXCA_MEM_WINS; win++) {
25989948Simp		if ((sc->memalloc & (1 << win)) == 0) {
26089948Simp			sc->memalloc |= (1 << win);
26189948Simp			break;
26289948Simp		}
26389948Simp	}
26489948Simp	if (win >= EXCA_MEM_WINS)
26589948Simp		return (1);
266120421Simp	if (((rman_get_start(res) >> EXCA_MEMREG_WIN_SHIFT) & 0xff) != 0 &&
267100703Simp	    (sc->flags & EXCA_HAS_MEMREG_WIN) == 0) {
268100703Simp		device_printf(sc->dev, "Does not support mapping above 24M.");
269100703Simp		return (1);
270100703Simp	}
27189948Simp
27289948Simp	sc->mem[win].cardaddr = 0;
27389948Simp	sc->mem[win].memt = rman_get_bustag(res);
27489948Simp	sc->mem[win].memh = rman_get_bushandle(res);
27589948Simp	sc->mem[win].addr = rman_get_start(res);
27689948Simp	sc->mem[win].size = rman_get_end(res) - sc->mem[win].addr + 1;
27789948Simp	sc->mem[win].realsize = sc->mem[win].size + EXCA_MEM_PAGESIZE - 1;
27889948Simp	sc->mem[win].realsize = sc->mem[win].realsize -
27989948Simp	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
28089948Simp	sc->mem[win].kind = kind;
281119520Simp	DPRINTF("exca_mem_map window %d bus %x+%x card addr %x\n",
282119520Simp	    win, sc->mem[win].addr, sc->mem[win].size, sc->mem[win].cardaddr);
28389948Simp	exca_do_mem_map(sc, win);
28489948Simp
28589948Simp	return (0);
28689948Simp}
28789948Simp
28889948Simp/*
28989948Simp * Private helper function.  This turns off a given memory map that is in
29089948Simp * use.  We do this by just clearing the enable bit in the pcic.  If we needed
29189948Simp * to make memory unmapping/mapping pairs faster, we would have to store
29289948Simp * more state information about the pcic and then use that to intelligently
29389948Simp * to the map/unmap.  However, since we don't do that sort of thing often
29489948Simp * (generally just at configure time), it isn't a case worth optimizing.
29589948Simp */
29689948Simpstatic void
29789948Simpexca_mem_unmap(struct exca_softc *sc, int window)
29889948Simp{
29989948Simp	if (window < 0 || window >= EXCA_MEM_WINS)
30089948Simp		panic("exca_mem_unmap: window out of range");
30189948Simp
30289948Simp	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, mem_map_index[window].memenable);
30389948Simp	sc->memalloc &= ~(1 << window);
30489948Simp}
30589948Simp
30689948Simp/*
30789948Simp * Find the map that we're using to hold the resoruce.  This works well
30889948Simp * so long as the client drivers don't do silly things like map the same
30989948Simp * area mutliple times, or map both common and attribute memory at the
31089948Simp * same time.  This latter restriction is a bug.  We likely should just
31189948Simp * store a pointer to the res in the mem[x] data structure.
31289948Simp */
31389948Simpstatic int
31489948Simpexca_mem_findmap(struct exca_softc *sc, struct resource *res)
31589948Simp{
31689948Simp	int win;
31789948Simp
31889948Simp	for (win = 0; win < EXCA_MEM_WINS; win++) {
31989948Simp		if (sc->mem[win].memt == rman_get_bustag(res) &&
32089948Simp		    sc->mem[win].addr == rman_get_start(res) &&
32189948Simp		    sc->mem[win].size == rman_get_size(res))
32289948Simp			return (win);
32389948Simp	}
32489948Simp	return (-1);
32589948Simp}
32689948Simp
32789948Simp/*
32889948Simp * Set the memory flag.  This means that we are setting if the memory
32989948Simp * is coming from attribute memory or from common memory on the card.
33089948Simp * CIS entries are generally in attribute memory (although they can
33189948Simp * reside in common memory).  Generally, this is the only use for attribute
33289948Simp * memory.  However, some cards require their drivers to dance in both
33389948Simp * common and/or attribute memory and this interface (and setting the
33489948Simp * offset interface) exist for such cards.
33589948Simp */
33689948Simpint
33789948Simpexca_mem_set_flags(struct exca_softc *sc, struct resource *res, uint32_t flags)
33889948Simp{
33989948Simp	int win;
34089948Simp
34189948Simp	win = exca_mem_findmap(sc, res);
34289948Simp	if (win < 0) {
34389948Simp		device_printf(sc->dev,
34489948Simp		    "set_res_flags: specified resource not active\n");
34589948Simp		return (ENOENT);
34689948Simp	}
34789948Simp
34889948Simp	sc->mem[win].kind = flags;
34989948Simp	exca_do_mem_map(sc, win);
35089948Simp	return (0);
35189948Simp}
35289948Simp
35389948Simp/*
35489948Simp * Given a resource, go ahead and unmap it if we can find it in the
35589948Simp * resrouce list that's used.
35689948Simp */
35789948Simpint
35889948Simpexca_mem_unmap_res(struct exca_softc *sc, struct resource *res)
35989948Simp{
36089948Simp	int win;
36189948Simp
36289948Simp	win = exca_mem_findmap(sc, res);
36389948Simp	if (win < 0)
36489948Simp		return (ENOENT);
36589948Simp	exca_mem_unmap(sc, win);
36689948Simp	return (0);
36789948Simp}
36889948Simp
36989948Simp/*
37089948Simp * Set the offset of the memory.  We use this for reading the CIS and
37189948Simp * frobbing the pccard's pccard registers (POR, etc).  Some drivers
37289948Simp * need to access this functionality as well, since they have receive
373119520Simp * buffers defined in the attribute memory.
37489948Simp */
37589948Simpint
37689948Simpexca_mem_set_offset(struct exca_softc *sc, struct resource *res,
37789948Simp    uint32_t cardaddr, uint32_t *deltap)
37889948Simp{
37989948Simp	int win;
38089948Simp	uint32_t delta;
38189948Simp
38289948Simp	win = exca_mem_findmap(sc, res);
38389948Simp	if (win < 0) {
38489948Simp		device_printf(sc->dev,
38589948Simp		    "set_memory_offset: specified resource not active\n");
38689948Simp		return (ENOENT);
38789948Simp	}
388119520Simp	sc->mem[win].cardaddr = cardaddr & ~(EXCA_MEM_PAGESIZE - 1);
38989948Simp	delta = cardaddr % EXCA_MEM_PAGESIZE;
39089948Simp	if (deltap)
39189948Simp		*deltap = delta;
39289948Simp	sc->mem[win].realsize = sc->mem[win].size + delta +
39389948Simp	    EXCA_MEM_PAGESIZE - 1;
39489948Simp	sc->mem[win].realsize = sc->mem[win].realsize -
39589948Simp	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
39689948Simp	exca_do_mem_map(sc, win);
39789948Simp	return (0);
39889948Simp}
39989948Simp
40089948Simp
40189948Simp/* I/O */
40289948Simp
40389948Simp#define	EXCA_IOINFO(NUM) {						\
40489948Simp	EXCA_IOADDR ## NUM ## _START_LSB,				\
40589948Simp	EXCA_IOADDR ## NUM ## _START_MSB,				\
40689948Simp	EXCA_IOADDR ## NUM ## _STOP_LSB,				\
40789948Simp	EXCA_IOADDR ## NUM ## _STOP_MSB,				\
40897708Salfred	EXCA_ADDRWIN_ENABLE_IO ## NUM,					\
40989948Simp	EXCA_IOCTL_IO ## NUM ## _WAITSTATE				\
41089948Simp	| EXCA_IOCTL_IO ## NUM ## _ZEROWAIT				\
41189948Simp	| EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_MASK			\
41289948Simp	| EXCA_IOCTL_IO ## NUM ## _DATASIZE_MASK,			\
41389948Simp	{								\
41489948Simp		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_CARD,		\
41589948Simp		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
41689948Simp		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_8BIT,		\
41789948Simp		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
41889948Simp		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_16BIT,		\
41989948Simp	}								\
42089948Simp}
42189948Simp
42289948Simpstatic struct io_map_index_st {
42389948Simp	int	start_lsb;
42489948Simp	int	start_msb;
42589948Simp	int	stop_lsb;
42689948Simp	int	stop_msb;
42789948Simp	int	ioenable;
42889948Simp	int	ioctlmask;
42989948Simp	int	ioctlbits[3]; /* indexed by PCCARD_WIDTH_* */
43089948Simp} io_map_index[] = {
43189948Simp	EXCA_IOINFO(0),
43289948Simp	EXCA_IOINFO(1),
43389948Simp};
43489948Simp#undef	EXCA_IOINFO
43589948Simp
43689948Simpstatic void
43789948Simpexca_do_io_map(struct exca_softc *sc, int win)
43889948Simp{
43989948Simp	struct io_map_index_st *map;
44089948Simp
44189948Simp	struct pccard_io_handle *io;
44289948Simp
44389948Simp	map = &io_map_index[win];
44489948Simp	io = &sc->io[win];
445110841Simp	exca_putb(sc, map->start_lsb, io->addr & 0xff);
446110841Simp	exca_putb(sc, map->start_msb, (io->addr >> 8) & 0xff);
44789948Simp
448110841Simp	exca_putb(sc, map->stop_lsb, (io->addr + io->size - 1) & 0xff);
449110841Simp	exca_putb(sc, map->stop_msb, ((io->addr + io->size - 1) >> 8) & 0xff);
45089948Simp
45189948Simp	exca_clrb(sc, EXCA_IOCTL, map->ioctlmask);
45289948Simp	exca_setb(sc, EXCA_IOCTL, map->ioctlbits[io->width]);
45389948Simp
45489948Simp	exca_setb(sc, EXCA_ADDRWIN_ENABLE, map->ioenable);
45589948Simp#ifdef EXCA_DEBUG
45689948Simp	{
45789948Simp		int r1, r2, r3, r4;
458110841Simp		r1 = exca_getb(sc, map->start_msb);
459110841Simp		r2 = exca_getb(sc, map->start_lsb);
460110841Simp		r3 = exca_getb(sc, map->stop_msb);
461110841Simp		r4 = exca_getb(sc, map->stop_lsb);
46289948Simp		DPRINTF("exca_do_io_map window %d: %02x%02x %02x%02x "
46389948Simp		    "(%08x+%08x)\n", win, r1, r2, r3, r4,
46489948Simp		    io->addr, io->size);
46589948Simp	}
46689948Simp#endif
46789948Simp}
46889948Simp
46989948Simpint
47089948Simpexca_io_map(struct exca_softc *sc, int width, struct resource *r)
47189948Simp{
47289948Simp	int win;
47389948Simp#ifdef EXCA_DEBUG
47489948Simp	static char *width_names[] = { "auto", "io8", "io16"};
47589948Simp#endif
47689948Simp	for (win=0; win < EXCA_IO_WINS; win++) {
47789948Simp		if ((sc->ioalloc & (1 << win)) == 0) {
47889948Simp			sc->ioalloc |= (1 << win);
47989948Simp			break;
48089948Simp		}
48189948Simp	}
48289948Simp	if (win >= EXCA_IO_WINS)
48389948Simp		return (1);
48489948Simp
48589948Simp	sc->io[win].iot = rman_get_bustag(r);
48689948Simp	sc->io[win].ioh = rman_get_bushandle(r);
48789948Simp	sc->io[win].addr = rman_get_start(r);
48889948Simp	sc->io[win].size = rman_get_end(r) - sc->io[win].addr + 1;
48989948Simp	sc->io[win].flags = 0;
49089948Simp	sc->io[win].width = width;
49189948Simp	DPRINTF("exca_io_map window %d %s port %x+%x\n",
49289948Simp	    win, width_names[width], sc->io[win].addr,
49389948Simp	    sc->io[win].size);
49489948Simp	exca_do_io_map(sc, win);
49589948Simp
49689948Simp	return (0);
49789948Simp}
49889948Simp
49989948Simpstatic void
50089948Simpexca_io_unmap(struct exca_softc *sc, int window)
50189948Simp{
50289948Simp	if (window >= EXCA_IO_WINS)
50389948Simp		panic("exca_io_unmap: window out of range");
50489948Simp
50589948Simp	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, io_map_index[window].ioenable);
50689948Simp
50789948Simp	sc->ioalloc &= ~(1 << window);
50889948Simp
50989948Simp	sc->io[window].iot = 0;
51089948Simp	sc->io[window].ioh = 0;
51189948Simp	sc->io[window].addr = 0;
51289948Simp	sc->io[window].size = 0;
51389948Simp	sc->io[window].flags = 0;
51489948Simp	sc->io[window].width = 0;
51589948Simp}
51689948Simp
51789948Simpstatic int
51889948Simpexca_io_findmap(struct exca_softc *sc, struct resource *res)
51989948Simp{
52089948Simp	int win;
52189948Simp
52289948Simp	for (win = 0; win < EXCA_IO_WINS; win++) {
52389948Simp		if (sc->io[win].iot == rman_get_bustag(res) &&
52489948Simp		    sc->io[win].addr == rman_get_start(res) &&
52589948Simp		    sc->io[win].size == rman_get_size(res))
52689948Simp			return (win);
52789948Simp	}
52889948Simp	return (-1);
52989948Simp}
53089948Simp
53189948Simp
53289948Simpint
53389948Simpexca_io_unmap_res(struct exca_softc *sc, struct resource *res)
53489948Simp{
53589948Simp	int win;
53689948Simp
53789948Simp	win = exca_io_findmap(sc, res);
53889948Simp	if (win < 0)
53989948Simp		return (ENOENT);
54089948Simp	exca_io_unmap(sc, win);
54189948Simp	return (0);
54289948Simp}
54389948Simp
54489948Simp/* Misc */
54589948Simp
54689948Simp/*
54789948Simp * If interrupts are enabled, then we should be able to just wait for
54889948Simp * an interrupt routine to wake us up.  Busy waiting shouldn't be
54989948Simp * necessary.  Sadly, not all legacy ISA cards support an interrupt
55089948Simp * for the busy state transitions, at least according to their datasheets,
55189948Simp * so we busy wait a while here..
55289948Simp */
55389948Simpstatic void
55489948Simpexca_wait_ready(struct exca_softc *sc)
55589948Simp{
55689948Simp	int i;
55789948Simp	DEVPRINTF(sc->dev, "exca_wait_ready: status 0x%02x\n",
558110841Simp	    exca_getb(sc, EXCA_IF_STATUS));
55989948Simp	for (i = 0; i < 10000; i++) {
560110841Simp		if (exca_getb(sc, EXCA_IF_STATUS) & EXCA_IF_STATUS_READY)
56189948Simp			return;
56289948Simp		DELAY(500);
56389948Simp	}
56489948Simp	device_printf(sc->dev, "ready never happened, status = %02x\n",
565110841Simp	    exca_getb(sc, EXCA_IF_STATUS));
56689948Simp}
56789948Simp
56889948Simp/*
56989948Simp * Reset the card.  Ideally, we'd do a lot of this via interrupts.
57089948Simp * However, many PC Cards will deassert the ready signal.  This means
57189948Simp * that they are asserting an interrupt.  This makes it hard to
57289948Simp * do anything but a busy wait here.  One could argue that these
57389948Simp * such cards are broken, or that the bridge that allows this sort
57489948Simp * of interrupt through isn't quite what you'd want (and may be a standards
575150460Simp * violation).  However, such arguing would leave a huge class of PC Cards
576104601Simp * and bridges out of reach for use in the system.
577104601Simp *
578104601Simp * Maybe I should reevaluate the above based on the power bug I fixed
579104601Simp * in OLDCARD.
58089948Simp */
58189948Simpvoid
58289948Simpexca_reset(struct exca_softc *sc, device_t child)
58389948Simp{
58489948Simp	int win;
58589948Simp
58689948Simp	/* enable socket i/o */
58789948Simp	exca_setb(sc, EXCA_PWRCTL, EXCA_PWRCTL_OE);
58889948Simp
589110841Simp	exca_putb(sc, EXCA_INTR, EXCA_INTR_ENABLE);
59089948Simp	/* hold reset for 30ms */
59189948Simp	DELAY(30*1000);
59289948Simp	/* clear the reset flag */
59389948Simp	exca_setb(sc, EXCA_INTR, EXCA_INTR_RESET);
594150460Simp	/* wait 20ms as per PC Card standard (r2.01) section 4.3.6 */
59589948Simp	DELAY(20*1000);
59689948Simp
59789948Simp	exca_wait_ready(sc);
59889948Simp
59989948Simp	/* disable all address windows */
600110841Simp	exca_putb(sc, EXCA_ADDRWIN_ENABLE, 0);
60189948Simp
602120872Simp	exca_setb(sc, EXCA_INTR, EXCA_INTR_CARDTYPE_IO);
603120872Simp	DEVPRINTF(sc->dev, "card type is io\n");
60489948Simp
60589948Simp	/* reinstall all the memory and io mappings */
60689948Simp	for (win = 0; win < EXCA_MEM_WINS; ++win)
60789948Simp		if (sc->memalloc & (1 << win))
60889948Simp			exca_do_mem_map(sc, win);
60989948Simp	for (win = 0; win < EXCA_IO_WINS; ++win)
61089948Simp		if (sc->ioalloc & (1 << win))
61189948Simp			exca_do_io_map(sc, win);
61289948Simp}
61389948Simp
61489948Simp/*
61589948Simp * Initialize the exca_softc data structure for the first time.
61689948Simp */
61789948Simpvoid
618100703Simpexca_init(struct exca_softc *sc, device_t dev,
619100703Simp    bus_space_tag_t bst, bus_space_handle_t bsh, uint32_t offset)
62089948Simp{
62189948Simp	sc->dev = dev;
62289948Simp	sc->memalloc = 0;
62389948Simp	sc->ioalloc = 0;
62489948Simp	sc->bst = bst;
62589948Simp	sc->bsh = bsh;
62689948Simp	sc->offset = offset;
62789948Simp	sc->flags = 0;
628110841Simp	sc->getb = exca_mem_getb;
629110841Simp	sc->putb = exca_mem_putb;
63089948Simp}
63189948Simp
63289948Simp/*
633110841Simp * Is this socket valid?
634110841Simp */
635110841Simpstatic int
636110841Simpexca_valid_slot(struct exca_softc *exca)
637110841Simp{
638110841Simp	uint8_t c;
639110841Simp
640115988Simp	/* Assume the worst */
641115988Simp	exca->chipset = EXCA_BOGUS;
642115988Simp
643110841Simp	/*
644110841Simp	 * see if there's a PCMCIA controller here
645110841Simp	 * Intel PCMCIA controllers use 0x82 and 0x83
646110841Simp	 * IBM clone chips use 0x88 and 0x89, apparently
647110841Simp	 */
648110841Simp	c = exca_getb(exca, EXCA_IDENT);
649151309Simp	DEVPRINTF(exca->dev, "Ident is %x\n", c);
650110841Simp	if ((c & EXCA_IDENT_IFTYPE_MASK) != EXCA_IDENT_IFTYPE_MEM_AND_IO)
651110841Simp		return (0);
652110841Simp	if ((c & EXCA_IDENT_ZERO) != 0)
653110841Simp		return (0);
654110841Simp	switch (c & EXCA_IDENT_REV_MASK) {
655110841Simp	/*
656110841Simp	 *	82365 or clones.
657110841Simp	 */
658110841Simp	case EXCA_IDENT_REV_I82365SLR0:
659110841Simp	case EXCA_IDENT_REV_I82365SLR1:
660110841Simp		exca->chipset = EXCA_I82365;
661110841Simp		/*
662110841Simp		 * Check for Vadem chips by unlocking their extra
663110841Simp		 * registers and looking for valid ID.  Bit 3 in
664110841Simp		 * the ID register is normally 0, except when
665110841Simp		 * EXCA_VADEMREV is set.  Other bridges appear
666110841Simp		 * to ignore this frobbing.
667110841Simp		 */
668110841Simp		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
669110841Simp		    EXCA_VADEM_COOKIE1);
670110841Simp		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
671110841Simp		    EXCA_VADEM_COOKIE2);
672110841Simp		exca_setb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
673110841Simp		c = exca_getb(exca, EXCA_IDENT);
674110841Simp		if (c & 0x08) {
675110841Simp			switch (c & 7) {
676110841Simp			case 1:
677110841Simp				exca->chipset = EXCA_VG365;
678110841Simp				break;
679110841Simp			case 2:
680110841Simp				exca->chipset = EXCA_VG465;
681110841Simp				break;
682110841Simp			case 3:
683110841Simp				exca->chipset = EXCA_VG468;
684110841Simp				break;
685110841Simp			default:
686110841Simp				exca->chipset = EXCA_VG469;
687110841Simp				break;
688110841Simp			}
689110841Simp			exca_clrb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
690110841Simp			break;
691110841Simp		}
692110841Simp		/*
693110841Simp		 * Check for RICOH RF5C[23]96 PCMCIA Controller
694110841Simp		 */
695110841Simp		c = exca_getb(exca, EXCA_RICOH_ID);
696110841Simp		if (c == EXCA_RID_396) {
697110841Simp			exca->chipset = EXCA_RF5C396;
698110841Simp			break;
699110841Simp		} else if (c == EXCA_RID_296) {
700110841Simp			exca->chipset = EXCA_RF5C296;
701110841Simp			break;
702110841Simp		}
703110841Simp		/*
704110841Simp		 *	Check for Cirrus logic chips.
705110841Simp		 */
706110841Simp		exca_putb(exca, EXCA_CIRRUS_CHIP_INFO, 0);
707110841Simp		c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
708110841Simp		if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) ==
709110841Simp		    EXCA_CIRRUS_CHIP_INFO_CHIP_ID) {
710110841Simp			c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
711110841Simp			if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) == 0) {
712110841Simp				if (c & EXCA_CIRRUS_CHIP_INFO_SLOTS)
713110841Simp					exca->chipset = EXCA_PD6722;
714110841Simp				else
715110841Simp					exca->chipset = EXCA_PD6710;
716110841Simp				break;
717110841Simp			}
718110841Simp		}
719110841Simp		break;
720110841Simp
721110841Simp	case EXCA_IDENT_REV_I82365SLDF:
722110841Simp		/*
723110841Simp		 *	Intel i82365sl-DF step or maybe a vlsi 82c146
724110841Simp		 * we detected the vlsi case earlier, so if the controller
725110841Simp		 * isn't set, we know it is a i82365sl step D.
726110841Simp		 */
727110841Simp		exca->chipset = EXCA_I82365SL_DF;
728110841Simp		break;
729110841Simp	case EXCA_IDENT_REV_IBM1:
730110841Simp	case EXCA_IDENT_REV_IBM2:
731110841Simp		exca->chipset = EXCA_IBM;
732110841Simp		break;
733110841Simp	case EXCA_IDENT_REV_IBM_KING:
734110841Simp		exca->chipset = EXCA_IBM_KING;
735110841Simp		break;
736110841Simp	default:
737110841Simp		return (0);
738110841Simp	}
739110841Simp	return (1);
740110841Simp}
741110841Simp
742110841Simp/*
74389948Simp * Probe the expected slots.  We maybe should set the ID for each of these
74489948Simp * slots too while we're at it.  But maybe that belongs to a separate
74589948Simp * function.
74689948Simp *
747110841Simp * The caller must guarantee that at least EXCA_NSLOTS are present in exca.
74889948Simp */
74989948Simpint
750110841Simpexca_probe_slots(device_t dev, struct exca_softc *exca, bus_space_tag_t iot,
751110841Simp    bus_space_handle_t ioh)
75289948Simp{
75389948Simp	int err;
75489948Simp	int i;
75589948Simp
75689948Simp	err = ENXIO;
757110841Simp	for (i = 0; i < EXCA_NSLOTS; i++)  {
758100703Simp		exca_init(&exca[i], dev, iot, ioh, i * EXCA_SOCKET_SIZE);
759110841Simp		exca->getb = exca_io_getb;
760110841Simp		exca->putb = exca_io_putb;
761110841Simp		if (exca_valid_slot(&exca[i]))
76289948Simp			err = 0;
76389948Simp	}
76489948Simp	return (err);
76589948Simp}
76689948Simp
767115988Simpvoid
768115988Simpexca_insert(struct exca_softc *exca)
769115988Simp{
770115988Simp	if (exca->pccarddev != NULL) {
771115988Simp		if (CARD_ATTACH_CARD(exca->pccarddev) != 0)
772115988Simp			device_printf(exca->dev,
773115988Simp			    "PC Card card activation failed\n");
774115988Simp	} else {
775115988Simp		device_printf(exca->dev,
776115988Simp		    "PC Card inserted, but no pccard bus.\n");
777115988Simp	}
778115988Simp}
779115988Simp
780115988Simp
781115988Simpvoid
782115988Simpexca_removal(struct exca_softc *exca)
783115988Simp{
784115988Simp	if (exca->pccarddev != NULL)
785115988Simp		CARD_DETACH_CARD(exca->pccarddev);
786115988Simp}
787115988Simp
788115988Simpint
789115988Simpexca_activate_resource(struct exca_softc *exca, device_t child, int type,
790115988Simp    int rid, struct resource *res)
791115988Simp{
792115988Simp	int err;
793115988Simp	if (!(rman_get_flags(res) & RF_ACTIVE)) { /* not already activated */
794115988Simp		switch (type) {
795115988Simp		case SYS_RES_IOPORT:
796119551Simp			err = exca_io_map(exca, PCCARD_WIDTH_AUTO, res);
797115988Simp			break;
798115988Simp		case SYS_RES_MEMORY:
799119551Simp			err = exca_mem_map(exca, PCCARD_A_MEM_COM, res);
800115988Simp			break;
801115988Simp		default:
802115988Simp			err = 0;
803115988Simp			break;
804115988Simp		}
805115988Simp		if (err)
806115988Simp			return (err);
807115988Simp
808115988Simp	}
809115988Simp	return (BUS_ACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
810115988Simp		  type, rid, res));
811115988Simp}
812115988Simp
813115988Simpint
814115988Simpexca_deactivate_resource(struct exca_softc *exca, device_t child, int type,
815115988Simp    int rid, struct resource *res)
816115988Simp{
817115988Simp	if (rman_get_flags(res) & RF_ACTIVE) { /* if activated */
818115988Simp		switch (type) {
819115988Simp		case SYS_RES_IOPORT:
820115988Simp			if (exca_io_unmap_res(exca, res))
821115988Simp				return (ENOENT);
822115988Simp			break;
823115988Simp		case SYS_RES_MEMORY:
824115988Simp			if (exca_mem_unmap_res(exca, res))
825115988Simp				return (ENOENT);
826115988Simp			break;
827115988Simp		}
828115988Simp	}
829115988Simp	return (BUS_DEACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
830115988Simp	    type, rid, res));
831115988Simp}
832115988Simp
833133782Simp#if 0
834133782Simpstatic struct resource *
835133782Simpexca_alloc_resource(struct exca_softc *sc, device_t child, int type, int *rid,
836133782Simp    u_long start, u_long end, u_long count, uint flags)
837133782Simp{
838133782Simp	struct resource *res = NULL;
839133782Simp	int tmp;
840133782Simp
841133782Simp	switch (type) {
842133782Simp	case SYS_RES_MEMORY:
843133782Simp		if (start < cbb_start_mem)
844133782Simp			start = cbb_start_mem;
845133782Simp		if (end < start)
846133782Simp			end = start;
847133782Simp		flags = (flags & ~RF_ALIGNMENT_MASK) |
848133782Simp		    rman_make_alignment_flags(CBB_MEMALIGN);
849133782Simp		break;
850133782Simp	case SYS_RES_IOPORT:
851133782Simp		if (start < cbb_start_16_io)
852133782Simp			start = cbb_start_16_io;
853133782Simp		if (end < start)
854133782Simp			end = start;
855133782Simp		break;
856133782Simp	case SYS_RES_IRQ:
857133782Simp		tmp = rman_get_start(sc->irq_res);
858133782Simp		if (start > tmp || end < tmp || count != 1) {
859133782Simp			device_printf(child, "requested interrupt %ld-%ld,"
860133782Simp			    "count = %ld not supported by cbb\n",
861133782Simp			    start, end, count);
862133782Simp			return (NULL);
863133782Simp		}
864133782Simp		flags |= RF_SHAREABLE;
865133782Simp		start = end = rman_get_start(sc->irq_res);
866133782Simp		break;
867133782Simp	}
868133782Simp	res = BUS_ALLOC_RESOURCE(up, child, type, rid,
869133782Simp	    start, end, count, flags & ~RF_ACTIVE);
870133782Simp	if (res == NULL)
871133782Simp		return (NULL);
872133782Simp	cbb_insert_res(sc, res, type, *rid);
873133782Simp	if (flags & RF_ACTIVE) {
874133782Simp		if (bus_activate_resource(child, type, *rid, res) != 0) {
875133782Simp			bus_release_resource(child, type, *rid, res);
876133782Simp			return (NULL);
877133782Simp		}
878133782Simp	}
879133782Simp
880133782Simp	return (res);
881133782Simp}
882133782Simp
883110841Simpstatic int
884133782Simpexca_release_resource(struct exca_softc *sc, device_t child, int type,
885133782Simp    int rid, struct resource *res)
886133782Simp{
887133782Simp	int error;
888133782Simp
889133782Simp	if (rman_get_flags(res) & RF_ACTIVE) {
890133782Simp		error = bus_deactivate_resource(child, type, rid, res);
891133782Simp		if (error != 0)
892133782Simp			return (error);
893133782Simp	}
894133782Simp	cbb_remove_res(sc, res);
895133782Simp	return (BUS_RELEASE_RESOURCE(device_get_parent(brdev), child,
896133782Simp	    type, rid, res));
897133782Simp}
898133782Simp#endif
899133782Simp
900133782Simpstatic int
901110841Simpexca_modevent(module_t mod, int cmd, void *arg)
90289948Simp{
903110841Simp	return 0;
90489948Simp}
90597613Stakawata
90697613StakawataDEV_MODULE(exca, exca_modevent, NULL);
90797613StakawataMODULE_VERSION(exca, 1);
908