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$");
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
89110841Simpstatic const char *chip_names[] =
90110841Simp{
91110841Simp	"CardBus socket",
92110841Simp	"Intel i82365SL-A/B or clone",
93110841Simp	"Intel i82365sl-DF step",
94110841Simp	"VLSI chip",
95110841Simp	"Cirrus Logic PD6710",
96110841Simp	"Cirrus logic PD6722",
97110841Simp	"Cirrus Logic PD6729",
98110841Simp	"Vadem 365",
99110841Simp	"Vadem 465",
100110841Simp	"Vadem 468",
101110841Simp	"Vadem 469",
102110841Simp	"Ricoh RF5C296",
103110841Simp	"Ricoh RF5C396",
104110841Simp	"IBM clone",
105110841Simp	"IBM KING PCMCIA Controller"
106110841Simp};
107110841Simp
108110841Simpstatic exca_getb_fn exca_mem_getb;
109110841Simpstatic exca_putb_fn exca_mem_putb;
110110841Simpstatic exca_getb_fn exca_io_getb;
111110841Simpstatic exca_putb_fn exca_io_putb;
112110841Simp
11389948Simp/* memory */
11489948Simp
11589948Simp#define	EXCA_MEMINFO(NUM) {						\
11689948Simp	EXCA_SYSMEM_ADDR ## NUM ## _START_LSB,				\
11789948Simp	EXCA_SYSMEM_ADDR ## NUM ## _START_MSB,				\
11889948Simp	EXCA_SYSMEM_ADDR ## NUM ## _STOP_LSB,				\
11989948Simp	EXCA_SYSMEM_ADDR ## NUM ## _STOP_MSB,				\
12089948Simp	EXCA_SYSMEM_ADDR ## NUM ## _WIN,				\
12189948Simp	EXCA_CARDMEM_ADDR ## NUM ## _LSB,				\
12289948Simp	EXCA_CARDMEM_ADDR ## NUM ## _MSB,				\
12397708Salfred	EXCA_ADDRWIN_ENABLE_MEM ## NUM,					\
12489948Simp}
12589948Simp
12689948Simpstatic struct mem_map_index_st {
12789948Simp	int	sysmem_start_lsb;
12889948Simp	int	sysmem_start_msb;
12989948Simp	int	sysmem_stop_lsb;
13089948Simp	int	sysmem_stop_msb;
13189948Simp	int	sysmem_win;
13289948Simp	int	cardmem_lsb;
13389948Simp	int	cardmem_msb;
13489948Simp	int	memenable;
13589948Simp} mem_map_index[] = {
13689948Simp	EXCA_MEMINFO(0),
13789948Simp	EXCA_MEMINFO(1),
13889948Simp	EXCA_MEMINFO(2),
13989948Simp	EXCA_MEMINFO(3),
14089948Simp	EXCA_MEMINFO(4)
14189948Simp};
14289948Simp#undef	EXCA_MEMINFO
14389948Simp
144110841Simpstatic uint8_t
145110841Simpexca_mem_getb(struct exca_softc *sc, int reg)
146110841Simp{
147110841Simp	return (bus_space_read_1(sc->bst, sc->bsh, sc->offset + reg));
148110841Simp}
149110841Simp
150110841Simpstatic void
151110841Simpexca_mem_putb(struct exca_softc *sc, int reg, uint8_t val)
152110841Simp{
153115461Sphk	bus_space_write_1(sc->bst, sc->bsh, sc->offset + reg, val);
154110841Simp}
155110841Simp
156110841Simpstatic uint8_t
157110841Simpexca_io_getb(struct exca_softc *sc, int reg)
158110841Simp{
159110841Simp	bus_space_write_1(sc->bst, sc->bsh, EXCA_REG_INDEX, reg + sc->offset);
160110841Simp	return (bus_space_read_1(sc->bst, sc->bsh, EXCA_REG_DATA));
161110841Simp}
162110841Simp
163110841Simpstatic void
164110841Simpexca_io_putb(struct exca_softc *sc, int reg, uint8_t val)
165110841Simp{
166110841Simp	bus_space_write_1(sc->bst, sc->bsh, EXCA_REG_INDEX, reg + sc->offset);
167110841Simp	bus_space_write_1(sc->bst, sc->bsh, EXCA_REG_DATA, val);
168110841Simp}
169110841Simp
17089948Simp/*
17189948Simp * Helper function.  This will map the requested memory slot.  We setup the
17289948Simp * map before we call this function.  This is used to initially force the
17389948Simp * mapping, as well as later restore the mapping after it has been destroyed
17489948Simp * in some fashion (due to a power event typically).
17589948Simp */
17689948Simpstatic void
17789948Simpexca_do_mem_map(struct exca_softc *sc, int win)
17889948Simp{
17989948Simp	struct mem_map_index_st *map;
18089948Simp	struct pccard_mem_handle *mem;
181140022Simp	uint32_t offset;
182186796Simp	uint32_t mem16;
183186796Simp	uint32_t attrmem;
18489948Simp
18589948Simp	map = &mem_map_index[win];
18689948Simp	mem = &sc->mem[win];
187186796Simp	mem16 = (mem->kind & PCCARD_MEM_16BIT) ?
188186796Simp	    EXCA_SYSMEM_ADDRX_START_MSB_DATASIZE_16BIT : 0;
189186796Simp	attrmem = (mem->kind & PCCARD_MEM_ATTR) ?
190186796Simp	    EXCA_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0;
191140022Simp	offset = ((mem->cardaddr >> EXCA_CARDMEM_ADDRX_SHIFT) -
192140022Simp	  (mem->addr >> EXCA_SYSMEM_ADDRX_SHIFT)) & 0x3fff;
193110841Simp	exca_putb(sc, map->sysmem_start_lsb,
194186796Simp	    mem->addr >> EXCA_SYSMEM_ADDRX_SHIFT);
195110841Simp	exca_putb(sc, map->sysmem_start_msb,
19689948Simp	    ((mem->addr >> (EXCA_SYSMEM_ADDRX_SHIFT + 8)) &
197186796Simp	    EXCA_SYSMEM_ADDRX_START_MSB_ADDR_MASK) | mem16);
19889948Simp
199110841Simp	exca_putb(sc, map->sysmem_stop_lsb,
200186796Simp	    (mem->addr + mem->realsize - 1) >> EXCA_SYSMEM_ADDRX_SHIFT);
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);
206186796Simp	exca_putb(sc, map->sysmem_win, mem->addr >> EXCA_MEMREG_WIN_SHIFT);
20789948Simp
208140022Simp	exca_putb(sc, map->cardmem_lsb, offset & 0xff);
209186796Simp	exca_putb(sc, map->cardmem_msb, ((offset >> 8) &
210186796Simp	    EXCA_CARDMEM_ADDRX_MSB_ADDR_MASK) | attrmem);
21189948Simp
212188167Simp	DPRINTF("%s %d-bit memory",
213188220Simp	    mem->kind & PCCARD_MEM_ATTR ? "attribute" : "common",
214188167Simp	    mem->kind & PCCARD_MEM_16BIT ? 16 : 8);
215133782Simp	exca_setb(sc, EXCA_ADDRWIN_ENABLE, map->memenable |
216133782Simp	    EXCA_ADDRWIN_ENABLE_MEMCS16);
21789948Simp
21889948Simp	DELAY(100);
21989948Simp#ifdef EXCA_DEBUG
22089948Simp	{
22189948Simp		int r1, r2, r3, r4, r5, r6, r7;
222110841Simp		r1 = exca_getb(sc, map->sysmem_start_msb);
223110841Simp		r2 = exca_getb(sc, map->sysmem_start_lsb);
224110841Simp		r3 = exca_getb(sc, map->sysmem_stop_msb);
225110841Simp		r4 = exca_getb(sc, map->sysmem_stop_lsb);
226110841Simp		r5 = exca_getb(sc, map->cardmem_msb);
227110841Simp		r6 = exca_getb(sc, map->cardmem_lsb);
228110841Simp		r7 = exca_getb(sc, map->sysmem_win);
229188167Simp		printf("exca_do_mem_map win %d: %#02x%#02x %#02x%#02x "
230188167Simp		    "%#02x%#02x %#02x (%#08x+%#06x.%#06x*%#06x) flags %#x\n",
23189948Simp		    win, r1, r2, r3, r4, r5, r6, r7,
23289948Simp		    mem->addr, mem->size, mem->realsize,
233188167Simp		    mem->cardaddr, mem->kind);
23489948Simp	}
23589948Simp#endif
23689948Simp}
23789948Simp
23889948Simp/*
23989948Simp * public interface to map a resource.  kind is the type of memory to
24089948Simp * map (either common or attribute).  Memory created via this interface
24189948Simp * starts out at card address 0.  Since the only way to set this is
24289948Simp * to set it on a struct resource after it has been mapped, we're safe
24389948Simp * in maping this assumption.  Note that resources can be remapped using
24489948Simp * exca_do_mem_map so that's how the card address can be set later.
24589948Simp */
24689948Simpint
24789948Simpexca_mem_map(struct exca_softc *sc, int kind, struct resource *res)
24889948Simp{
24989948Simp	int win;
25089948Simp
25189948Simp	for (win = 0; win < EXCA_MEM_WINS; win++) {
25289948Simp		if ((sc->memalloc & (1 << win)) == 0) {
25389948Simp			sc->memalloc |= (1 << win);
25489948Simp			break;
25589948Simp		}
25689948Simp	}
25789948Simp	if (win >= EXCA_MEM_WINS)
258167832Sjhb		return (ENOSPC);
259188167Simp	if (sc->flags & EXCA_HAS_MEMREG_WIN) {
260189579Simp#ifdef __LP64__
261188167Simp		if (rman_get_start(res) >> (EXCA_MEMREG_WIN_SHIFT + 8) != 0) {
262188167Simp			device_printf(sc->dev,
263188167Simp			    "Does not support mapping above 4GB.");
264188167Simp			return (EINVAL);
265188167Simp		}
266188190Simp#endif
267188167Simp	} else {
268188167Simp		if (rman_get_start(res) >> EXCA_MEMREG_WIN_SHIFT != 0) {
269188167Simp			device_printf(sc->dev,
270188167Simp			    "Does not support mapping above 16M.");
271188167Simp			return (EINVAL);
272188167Simp		}
273100703Simp	}
27489948Simp
27589948Simp	sc->mem[win].cardaddr = 0;
27689948Simp	sc->mem[win].memt = rman_get_bustag(res);
27789948Simp	sc->mem[win].memh = rman_get_bushandle(res);
27889948Simp	sc->mem[win].addr = rman_get_start(res);
27989948Simp	sc->mem[win].size = rman_get_end(res) - sc->mem[win].addr + 1;
28089948Simp	sc->mem[win].realsize = sc->mem[win].size + EXCA_MEM_PAGESIZE - 1;
28189948Simp	sc->mem[win].realsize = sc->mem[win].realsize -
28289948Simp	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
28389948Simp	sc->mem[win].kind = kind;
284119520Simp	DPRINTF("exca_mem_map window %d bus %x+%x card addr %x\n",
285119520Simp	    win, sc->mem[win].addr, sc->mem[win].size, sc->mem[win].cardaddr);
28689948Simp	exca_do_mem_map(sc, win);
28789948Simp
28889948Simp	return (0);
28989948Simp}
29089948Simp
29189948Simp/*
29289948Simp * Private helper function.  This turns off a given memory map that is in
29389948Simp * use.  We do this by just clearing the enable bit in the pcic.  If we needed
29489948Simp * to make memory unmapping/mapping pairs faster, we would have to store
29589948Simp * more state information about the pcic and then use that to intelligently
29689948Simp * to the map/unmap.  However, since we don't do that sort of thing often
29789948Simp * (generally just at configure time), it isn't a case worth optimizing.
29889948Simp */
29989948Simpstatic void
30089948Simpexca_mem_unmap(struct exca_softc *sc, int window)
30189948Simp{
30289948Simp	if (window < 0 || window >= EXCA_MEM_WINS)
30389948Simp		panic("exca_mem_unmap: window out of range");
30489948Simp
30589948Simp	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, mem_map_index[window].memenable);
30689948Simp	sc->memalloc &= ~(1 << window);
30789948Simp}
30889948Simp
30989948Simp/*
310167832Sjhb * Find the map that we're using to hold the resource.  This works well
31189948Simp * so long as the client drivers don't do silly things like map the same
31289948Simp * area mutliple times, or map both common and attribute memory at the
31389948Simp * same time.  This latter restriction is a bug.  We likely should just
31489948Simp * store a pointer to the res in the mem[x] data structure.
31589948Simp */
31689948Simpstatic int
31789948Simpexca_mem_findmap(struct exca_softc *sc, struct resource *res)
31889948Simp{
31989948Simp	int win;
32089948Simp
32189948Simp	for (win = 0; win < EXCA_MEM_WINS; win++) {
32289948Simp		if (sc->mem[win].memt == rman_get_bustag(res) &&
32389948Simp		    sc->mem[win].addr == rman_get_start(res) &&
32489948Simp		    sc->mem[win].size == rman_get_size(res))
32589948Simp			return (win);
32689948Simp	}
32789948Simp	return (-1);
32889948Simp}
32989948Simp
33089948Simp/*
33189948Simp * Set the memory flag.  This means that we are setting if the memory
33289948Simp * is coming from attribute memory or from common memory on the card.
33389948Simp * CIS entries are generally in attribute memory (although they can
33489948Simp * reside in common memory).  Generally, this is the only use for attribute
33589948Simp * memory.  However, some cards require their drivers to dance in both
33689948Simp * common and/or attribute memory and this interface (and setting the
33789948Simp * offset interface) exist for such cards.
33889948Simp */
33989948Simpint
34089948Simpexca_mem_set_flags(struct exca_softc *sc, struct resource *res, uint32_t flags)
34189948Simp{
34289948Simp	int win;
34389948Simp
34489948Simp	win = exca_mem_findmap(sc, res);
34589948Simp	if (win < 0) {
34689948Simp		device_printf(sc->dev,
34789948Simp		    "set_res_flags: specified resource not active\n");
34889948Simp		return (ENOENT);
34989948Simp	}
35089948Simp
351186796Simp	switch (flags)
352186796Simp	{
353186796Simp	case PCCARD_A_MEM_ATTR:
354186796Simp		sc->mem[win].kind |= PCCARD_MEM_ATTR;
355186796Simp		break;
356186796Simp	case PCCARD_A_MEM_COM:
357186796Simp		sc->mem[win].kind &= ~PCCARD_MEM_ATTR;
358186796Simp		break;
359186796Simp	case PCCARD_A_MEM_16BIT:
360186796Simp		sc->mem[win].kind |= PCCARD_MEM_16BIT;
361186796Simp		break;
362186796Simp	case PCCARD_A_MEM_8BIT:
363186796Simp		sc->mem[win].kind &= ~PCCARD_MEM_16BIT;
364186796Simp		break;
365186796Simp	}
36689948Simp	exca_do_mem_map(sc, win);
36789948Simp	return (0);
36889948Simp}
36989948Simp
37089948Simp/*
37189948Simp * Given a resource, go ahead and unmap it if we can find it in the
37289948Simp * resrouce list that's used.
37389948Simp */
37489948Simpint
37589948Simpexca_mem_unmap_res(struct exca_softc *sc, struct resource *res)
37689948Simp{
37789948Simp	int win;
37889948Simp
37989948Simp	win = exca_mem_findmap(sc, res);
38089948Simp	if (win < 0)
38189948Simp		return (ENOENT);
38289948Simp	exca_mem_unmap(sc, win);
38389948Simp	return (0);
38489948Simp}
38589948Simp
38689948Simp/*
38789948Simp * Set the offset of the memory.  We use this for reading the CIS and
388151410Simp * frobbing the pccard's pccard registers (CCR, etc).  Some drivers
389151410Simp * need to access arbitrary attribute and common memory during their
390151410Simp * initialization and operation.
39189948Simp */
39289948Simpint
39389948Simpexca_mem_set_offset(struct exca_softc *sc, struct resource *res,
39489948Simp    uint32_t cardaddr, uint32_t *deltap)
39589948Simp{
39689948Simp	int win;
39789948Simp	uint32_t delta;
39889948Simp
39989948Simp	win = exca_mem_findmap(sc, res);
40089948Simp	if (win < 0) {
40189948Simp		device_printf(sc->dev,
40289948Simp		    "set_memory_offset: specified resource not active\n");
40389948Simp		return (ENOENT);
40489948Simp	}
405119520Simp	sc->mem[win].cardaddr = cardaddr & ~(EXCA_MEM_PAGESIZE - 1);
40689948Simp	delta = cardaddr % EXCA_MEM_PAGESIZE;
40789948Simp	if (deltap)
40889948Simp		*deltap = delta;
40989948Simp	sc->mem[win].realsize = sc->mem[win].size + delta +
41089948Simp	    EXCA_MEM_PAGESIZE - 1;
41189948Simp	sc->mem[win].realsize = sc->mem[win].realsize -
41289948Simp	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
41389948Simp	exca_do_mem_map(sc, win);
41489948Simp	return (0);
41589948Simp}
41689948Simp
41789948Simp
41889948Simp/* I/O */
41989948Simp
42089948Simp#define	EXCA_IOINFO(NUM) {						\
42189948Simp	EXCA_IOADDR ## NUM ## _START_LSB,				\
42289948Simp	EXCA_IOADDR ## NUM ## _START_MSB,				\
42389948Simp	EXCA_IOADDR ## NUM ## _STOP_LSB,				\
42489948Simp	EXCA_IOADDR ## NUM ## _STOP_MSB,				\
42597708Salfred	EXCA_ADDRWIN_ENABLE_IO ## NUM,					\
42689948Simp	EXCA_IOCTL_IO ## NUM ## _WAITSTATE				\
42789948Simp	| EXCA_IOCTL_IO ## NUM ## _ZEROWAIT				\
42889948Simp	| EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_MASK			\
42989948Simp	| EXCA_IOCTL_IO ## NUM ## _DATASIZE_MASK,			\
43089948Simp	{								\
43189948Simp		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_CARD,		\
43289948Simp		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
43389948Simp		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_8BIT,		\
43489948Simp		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
43589948Simp		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_16BIT,		\
43689948Simp	}								\
43789948Simp}
43889948Simp
43989948Simpstatic struct io_map_index_st {
44089948Simp	int	start_lsb;
44189948Simp	int	start_msb;
44289948Simp	int	stop_lsb;
44389948Simp	int	stop_msb;
44489948Simp	int	ioenable;
44589948Simp	int	ioctlmask;
44689948Simp	int	ioctlbits[3]; /* indexed by PCCARD_WIDTH_* */
44789948Simp} io_map_index[] = {
44889948Simp	EXCA_IOINFO(0),
44989948Simp	EXCA_IOINFO(1),
45089948Simp};
45189948Simp#undef	EXCA_IOINFO
45289948Simp
45389948Simpstatic void
45489948Simpexca_do_io_map(struct exca_softc *sc, int win)
45589948Simp{
45689948Simp	struct io_map_index_st *map;
45789948Simp
45889948Simp	struct pccard_io_handle *io;
45989948Simp
46089948Simp	map = &io_map_index[win];
46189948Simp	io = &sc->io[win];
462110841Simp	exca_putb(sc, map->start_lsb, io->addr & 0xff);
463110841Simp	exca_putb(sc, map->start_msb, (io->addr >> 8) & 0xff);
46489948Simp
465110841Simp	exca_putb(sc, map->stop_lsb, (io->addr + io->size - 1) & 0xff);
466110841Simp	exca_putb(sc, map->stop_msb, ((io->addr + io->size - 1) >> 8) & 0xff);
46789948Simp
46889948Simp	exca_clrb(sc, EXCA_IOCTL, map->ioctlmask);
46989948Simp	exca_setb(sc, EXCA_IOCTL, map->ioctlbits[io->width]);
47089948Simp
47189948Simp	exca_setb(sc, EXCA_ADDRWIN_ENABLE, map->ioenable);
47289948Simp#ifdef EXCA_DEBUG
47389948Simp	{
47489948Simp		int r1, r2, r3, r4;
475110841Simp		r1 = exca_getb(sc, map->start_msb);
476110841Simp		r2 = exca_getb(sc, map->start_lsb);
477110841Simp		r3 = exca_getb(sc, map->stop_msb);
478110841Simp		r4 = exca_getb(sc, map->stop_lsb);
47989948Simp		DPRINTF("exca_do_io_map window %d: %02x%02x %02x%02x "
48089948Simp		    "(%08x+%08x)\n", win, r1, r2, r3, r4,
48189948Simp		    io->addr, io->size);
48289948Simp	}
48389948Simp#endif
48489948Simp}
48589948Simp
48689948Simpint
48789948Simpexca_io_map(struct exca_softc *sc, int width, struct resource *r)
48889948Simp{
48989948Simp	int win;
49089948Simp#ifdef EXCA_DEBUG
49189948Simp	static char *width_names[] = { "auto", "io8", "io16"};
49289948Simp#endif
49389948Simp	for (win=0; win < EXCA_IO_WINS; win++) {
49489948Simp		if ((sc->ioalloc & (1 << win)) == 0) {
49589948Simp			sc->ioalloc |= (1 << win);
49689948Simp			break;
49789948Simp		}
49889948Simp	}
49989948Simp	if (win >= EXCA_IO_WINS)
500167832Sjhb		return (ENOSPC);
50189948Simp
50289948Simp	sc->io[win].iot = rman_get_bustag(r);
50389948Simp	sc->io[win].ioh = rman_get_bushandle(r);
50489948Simp	sc->io[win].addr = rman_get_start(r);
50589948Simp	sc->io[win].size = rman_get_end(r) - sc->io[win].addr + 1;
50689948Simp	sc->io[win].flags = 0;
50789948Simp	sc->io[win].width = width;
50889948Simp	DPRINTF("exca_io_map window %d %s port %x+%x\n",
50989948Simp	    win, width_names[width], sc->io[win].addr,
51089948Simp	    sc->io[win].size);
51189948Simp	exca_do_io_map(sc, win);
51289948Simp
51389948Simp	return (0);
51489948Simp}
51589948Simp
51689948Simpstatic void
51789948Simpexca_io_unmap(struct exca_softc *sc, int window)
51889948Simp{
51989948Simp	if (window >= EXCA_IO_WINS)
52089948Simp		panic("exca_io_unmap: window out of range");
52189948Simp
52289948Simp	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, io_map_index[window].ioenable);
52389948Simp
52489948Simp	sc->ioalloc &= ~(1 << window);
52589948Simp
52689948Simp	sc->io[window].iot = 0;
52789948Simp	sc->io[window].ioh = 0;
52889948Simp	sc->io[window].addr = 0;
52989948Simp	sc->io[window].size = 0;
53089948Simp	sc->io[window].flags = 0;
53189948Simp	sc->io[window].width = 0;
53289948Simp}
53389948Simp
53489948Simpstatic int
53589948Simpexca_io_findmap(struct exca_softc *sc, struct resource *res)
53689948Simp{
53789948Simp	int win;
53889948Simp
53989948Simp	for (win = 0; win < EXCA_IO_WINS; win++) {
54089948Simp		if (sc->io[win].iot == rman_get_bustag(res) &&
54189948Simp		    sc->io[win].addr == rman_get_start(res) &&
54289948Simp		    sc->io[win].size == rman_get_size(res))
54389948Simp			return (win);
54489948Simp	}
54589948Simp	return (-1);
54689948Simp}
54789948Simp
54889948Simp
54989948Simpint
55089948Simpexca_io_unmap_res(struct exca_softc *sc, struct resource *res)
55189948Simp{
55289948Simp	int win;
55389948Simp
55489948Simp	win = exca_io_findmap(sc, res);
55589948Simp	if (win < 0)
55689948Simp		return (ENOENT);
55789948Simp	exca_io_unmap(sc, win);
55889948Simp	return (0);
55989948Simp}
56089948Simp
56189948Simp/* Misc */
56289948Simp
56389948Simp/*
56489948Simp * If interrupts are enabled, then we should be able to just wait for
56589948Simp * an interrupt routine to wake us up.  Busy waiting shouldn't be
56689948Simp * necessary.  Sadly, not all legacy ISA cards support an interrupt
56789948Simp * for the busy state transitions, at least according to their datasheets,
56889948Simp * so we busy wait a while here..
56989948Simp */
57089948Simpstatic void
57189948Simpexca_wait_ready(struct exca_softc *sc)
57289948Simp{
57389948Simp	int i;
57489948Simp	DEVPRINTF(sc->dev, "exca_wait_ready: status 0x%02x\n",
575110841Simp	    exca_getb(sc, EXCA_IF_STATUS));
57689948Simp	for (i = 0; i < 10000; i++) {
577110841Simp		if (exca_getb(sc, EXCA_IF_STATUS) & EXCA_IF_STATUS_READY)
57889948Simp			return;
57989948Simp		DELAY(500);
58089948Simp	}
58189948Simp	device_printf(sc->dev, "ready never happened, status = %02x\n",
582110841Simp	    exca_getb(sc, EXCA_IF_STATUS));
58389948Simp}
58489948Simp
58589948Simp/*
58689948Simp * Reset the card.  Ideally, we'd do a lot of this via interrupts.
58789948Simp * However, many PC Cards will deassert the ready signal.  This means
58889948Simp * that they are asserting an interrupt.  This makes it hard to
58989948Simp * do anything but a busy wait here.  One could argue that these
59089948Simp * such cards are broken, or that the bridge that allows this sort
59189948Simp * of interrupt through isn't quite what you'd want (and may be a standards
592150460Simp * violation).  However, such arguing would leave a huge class of PC Cards
593104601Simp * and bridges out of reach for use in the system.
594104601Simp *
595104601Simp * Maybe I should reevaluate the above based on the power bug I fixed
596104601Simp * in OLDCARD.
59789948Simp */
59889948Simpvoid
59989948Simpexca_reset(struct exca_softc *sc, device_t child)
60089948Simp{
60189948Simp	int win;
60289948Simp
60389948Simp	/* enable socket i/o */
60489948Simp	exca_setb(sc, EXCA_PWRCTL, EXCA_PWRCTL_OE);
60589948Simp
606110841Simp	exca_putb(sc, EXCA_INTR, EXCA_INTR_ENABLE);
60789948Simp	/* hold reset for 30ms */
60889948Simp	DELAY(30*1000);
60989948Simp	/* clear the reset flag */
61089948Simp	exca_setb(sc, EXCA_INTR, EXCA_INTR_RESET);
611150460Simp	/* wait 20ms as per PC Card standard (r2.01) section 4.3.6 */
61289948Simp	DELAY(20*1000);
61389948Simp
61489948Simp	exca_wait_ready(sc);
61589948Simp
61689948Simp	/* disable all address windows */
617110841Simp	exca_putb(sc, EXCA_ADDRWIN_ENABLE, 0);
61889948Simp
619120872Simp	exca_setb(sc, EXCA_INTR, EXCA_INTR_CARDTYPE_IO);
620120872Simp	DEVPRINTF(sc->dev, "card type is io\n");
62189948Simp
62289948Simp	/* reinstall all the memory and io mappings */
62389948Simp	for (win = 0; win < EXCA_MEM_WINS; ++win)
62489948Simp		if (sc->memalloc & (1 << win))
62589948Simp			exca_do_mem_map(sc, win);
62689948Simp	for (win = 0; win < EXCA_IO_WINS; ++win)
62789948Simp		if (sc->ioalloc & (1 << win))
62889948Simp			exca_do_io_map(sc, win);
62989948Simp}
63089948Simp
63189948Simp/*
63289948Simp * Initialize the exca_softc data structure for the first time.
63389948Simp */
63489948Simpvoid
635100703Simpexca_init(struct exca_softc *sc, device_t dev,
636100703Simp    bus_space_tag_t bst, bus_space_handle_t bsh, uint32_t offset)
63789948Simp{
63889948Simp	sc->dev = dev;
63989948Simp	sc->memalloc = 0;
64089948Simp	sc->ioalloc = 0;
64189948Simp	sc->bst = bst;
64289948Simp	sc->bsh = bsh;
64389948Simp	sc->offset = offset;
64489948Simp	sc->flags = 0;
645110841Simp	sc->getb = exca_mem_getb;
646110841Simp	sc->putb = exca_mem_putb;
64789948Simp}
64889948Simp
64989948Simp/*
650110841Simp * Is this socket valid?
651110841Simp */
652110841Simpstatic int
653110841Simpexca_valid_slot(struct exca_softc *exca)
654110841Simp{
655110841Simp	uint8_t c;
656110841Simp
657115988Simp	/* Assume the worst */
658115988Simp	exca->chipset = EXCA_BOGUS;
659115988Simp
660110841Simp	/*
661110841Simp	 * see if there's a PCMCIA controller here
662110841Simp	 * Intel PCMCIA controllers use 0x82 and 0x83
663110841Simp	 * IBM clone chips use 0x88 and 0x89, apparently
664110841Simp	 */
665110841Simp	c = exca_getb(exca, EXCA_IDENT);
666151309Simp	DEVPRINTF(exca->dev, "Ident is %x\n", c);
667110841Simp	if ((c & EXCA_IDENT_IFTYPE_MASK) != EXCA_IDENT_IFTYPE_MEM_AND_IO)
668110841Simp		return (0);
669110841Simp	if ((c & EXCA_IDENT_ZERO) != 0)
670110841Simp		return (0);
671110841Simp	switch (c & EXCA_IDENT_REV_MASK) {
672110841Simp	/*
673110841Simp	 *	82365 or clones.
674110841Simp	 */
675110841Simp	case EXCA_IDENT_REV_I82365SLR0:
676110841Simp	case EXCA_IDENT_REV_I82365SLR1:
677110841Simp		exca->chipset = EXCA_I82365;
678110841Simp		/*
679110841Simp		 * Check for Vadem chips by unlocking their extra
680110841Simp		 * registers and looking for valid ID.  Bit 3 in
681110841Simp		 * the ID register is normally 0, except when
682110841Simp		 * EXCA_VADEMREV is set.  Other bridges appear
683110841Simp		 * to ignore this frobbing.
684110841Simp		 */
685110841Simp		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
686110841Simp		    EXCA_VADEM_COOKIE1);
687110841Simp		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
688110841Simp		    EXCA_VADEM_COOKIE2);
689110841Simp		exca_setb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
690110841Simp		c = exca_getb(exca, EXCA_IDENT);
691110841Simp		if (c & 0x08) {
692110841Simp			switch (c & 7) {
693110841Simp			case 1:
694110841Simp				exca->chipset = EXCA_VG365;
695110841Simp				break;
696110841Simp			case 2:
697110841Simp				exca->chipset = EXCA_VG465;
698110841Simp				break;
699110841Simp			case 3:
700110841Simp				exca->chipset = EXCA_VG468;
701110841Simp				break;
702110841Simp			default:
703110841Simp				exca->chipset = EXCA_VG469;
704110841Simp				break;
705110841Simp			}
706110841Simp			exca_clrb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
707110841Simp			break;
708110841Simp		}
709110841Simp		/*
710110841Simp		 * Check for RICOH RF5C[23]96 PCMCIA Controller
711110841Simp		 */
712110841Simp		c = exca_getb(exca, EXCA_RICOH_ID);
713110841Simp		if (c == EXCA_RID_396) {
714110841Simp			exca->chipset = EXCA_RF5C396;
715110841Simp			break;
716110841Simp		} else if (c == EXCA_RID_296) {
717110841Simp			exca->chipset = EXCA_RF5C296;
718110841Simp			break;
719110841Simp		}
720110841Simp		/*
721110841Simp		 *	Check for Cirrus logic chips.
722110841Simp		 */
723110841Simp		exca_putb(exca, EXCA_CIRRUS_CHIP_INFO, 0);
724110841Simp		c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
725110841Simp		if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) ==
726110841Simp		    EXCA_CIRRUS_CHIP_INFO_CHIP_ID) {
727110841Simp			c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
728110841Simp			if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) == 0) {
729110841Simp				if (c & EXCA_CIRRUS_CHIP_INFO_SLOTS)
730110841Simp					exca->chipset = EXCA_PD6722;
731110841Simp				else
732110841Simp					exca->chipset = EXCA_PD6710;
733110841Simp				break;
734110841Simp			}
735110841Simp		}
736110841Simp		break;
737110841Simp
738110841Simp	case EXCA_IDENT_REV_I82365SLDF:
739110841Simp		/*
740110841Simp		 *	Intel i82365sl-DF step or maybe a vlsi 82c146
741110841Simp		 * we detected the vlsi case earlier, so if the controller
742110841Simp		 * isn't set, we know it is a i82365sl step D.
743110841Simp		 */
744110841Simp		exca->chipset = EXCA_I82365SL_DF;
745110841Simp		break;
746110841Simp	case EXCA_IDENT_REV_IBM1:
747110841Simp	case EXCA_IDENT_REV_IBM2:
748110841Simp		exca->chipset = EXCA_IBM;
749110841Simp		break;
750110841Simp	case EXCA_IDENT_REV_IBM_KING:
751110841Simp		exca->chipset = EXCA_IBM_KING;
752110841Simp		break;
753110841Simp	default:
754110841Simp		return (0);
755110841Simp	}
756110841Simp	return (1);
757110841Simp}
758110841Simp
759110841Simp/*
76089948Simp * Probe the expected slots.  We maybe should set the ID for each of these
76189948Simp * slots too while we're at it.  But maybe that belongs to a separate
76289948Simp * function.
76389948Simp *
764110841Simp * The caller must guarantee that at least EXCA_NSLOTS are present in exca.
76589948Simp */
76689948Simpint
767110841Simpexca_probe_slots(device_t dev, struct exca_softc *exca, bus_space_tag_t iot,
768110841Simp    bus_space_handle_t ioh)
76989948Simp{
77089948Simp	int err;
77189948Simp	int i;
77289948Simp
77389948Simp	err = ENXIO;
774110841Simp	for (i = 0; i < EXCA_NSLOTS; i++)  {
775100703Simp		exca_init(&exca[i], dev, iot, ioh, i * EXCA_SOCKET_SIZE);
776110841Simp		exca->getb = exca_io_getb;
777110841Simp		exca->putb = exca_io_putb;
778151458Simp		if (exca_valid_slot(&exca[i])) {
779151458Simp			device_set_desc(dev, chip_names[exca[i].chipset]);
78089948Simp			err = 0;
781151458Simp		}
78289948Simp	}
78389948Simp	return (err);
78489948Simp}
78589948Simp
786115988Simpvoid
787115988Simpexca_insert(struct exca_softc *exca)
788115988Simp{
789166942Simp	if (device_is_attached(exca->pccarddev)) {
790115988Simp		if (CARD_ATTACH_CARD(exca->pccarddev) != 0)
791115988Simp			device_printf(exca->dev,
792115988Simp			    "PC Card card activation failed\n");
793115988Simp	} else {
794115988Simp		device_printf(exca->dev,
795115988Simp		    "PC Card inserted, but no pccard bus.\n");
796115988Simp	}
797115988Simp}
798115988Simp
799115988Simp
800115988Simpvoid
801115988Simpexca_removal(struct exca_softc *exca)
802115988Simp{
803166742Simp	if (device_is_attached(exca->pccarddev))
804115988Simp		CARD_DETACH_CARD(exca->pccarddev);
805115988Simp}
806115988Simp
807115988Simpint
808115988Simpexca_activate_resource(struct exca_softc *exca, device_t child, int type,
809115988Simp    int rid, struct resource *res)
810115988Simp{
811115988Simp	int err;
812115988Simp
813167832Sjhb	if (rman_get_flags(res) & RF_ACTIVE)
814167832Sjhb		return (0);
815167832Sjhb	err = BUS_ACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
816167832Sjhb	    type, rid, res);
817167832Sjhb	if (err)
818167832Sjhb		return (err);
819167832Sjhb	switch (type) {
820167832Sjhb	case SYS_RES_IOPORT:
821167832Sjhb		err = exca_io_map(exca, PCCARD_WIDTH_AUTO, res);
822167832Sjhb		break;
823167832Sjhb	case SYS_RES_MEMORY:
824186796Simp		err = exca_mem_map(exca, 0, res);
825167832Sjhb		break;
826115988Simp	}
827167832Sjhb	if (err)
828167832Sjhb		BUS_DEACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
829167832Sjhb		    type, rid, res);
830167832Sjhb	return (err);
831115988Simp}
832115988Simp
833115988Simpint
834115988Simpexca_deactivate_resource(struct exca_softc *exca, device_t child, int type,
835115988Simp    int rid, struct resource *res)
836115988Simp{
837115988Simp	if (rman_get_flags(res) & RF_ACTIVE) { /* if activated */
838115988Simp		switch (type) {
839115988Simp		case SYS_RES_IOPORT:
840115988Simp			if (exca_io_unmap_res(exca, res))
841115988Simp				return (ENOENT);
842115988Simp			break;
843115988Simp		case SYS_RES_MEMORY:
844115988Simp			if (exca_mem_unmap_res(exca, res))
845115988Simp				return (ENOENT);
846115988Simp			break;
847115988Simp		}
848115988Simp	}
849115988Simp	return (BUS_DEACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
850115988Simp	    type, rid, res));
851115988Simp}
852115988Simp
853133782Simp#if 0
854133782Simpstatic struct resource *
855133782Simpexca_alloc_resource(struct exca_softc *sc, device_t child, int type, int *rid,
856133782Simp    u_long start, u_long end, u_long count, uint flags)
857133782Simp{
858133782Simp	struct resource *res = NULL;
859133782Simp	int tmp;
860133782Simp
861133782Simp	switch (type) {
862133782Simp	case SYS_RES_MEMORY:
863133782Simp		if (start < cbb_start_mem)
864133782Simp			start = cbb_start_mem;
865133782Simp		if (end < start)
866133782Simp			end = start;
867133782Simp		flags = (flags & ~RF_ALIGNMENT_MASK) |
868133782Simp		    rman_make_alignment_flags(CBB_MEMALIGN);
869133782Simp		break;
870133782Simp	case SYS_RES_IOPORT:
871133782Simp		if (start < cbb_start_16_io)
872133782Simp			start = cbb_start_16_io;
873133782Simp		if (end < start)
874133782Simp			end = start;
875133782Simp		break;
876133782Simp	case SYS_RES_IRQ:
877133782Simp		tmp = rman_get_start(sc->irq_res);
878133782Simp		if (start > tmp || end < tmp || count != 1) {
879133782Simp			device_printf(child, "requested interrupt %ld-%ld,"
880133782Simp			    "count = %ld not supported by cbb\n",
881133782Simp			    start, end, count);
882133782Simp			return (NULL);
883133782Simp		}
884133782Simp		flags |= RF_SHAREABLE;
885133782Simp		start = end = rman_get_start(sc->irq_res);
886133782Simp		break;
887133782Simp	}
888133782Simp	res = BUS_ALLOC_RESOURCE(up, child, type, rid,
889133782Simp	    start, end, count, flags & ~RF_ACTIVE);
890133782Simp	if (res == NULL)
891133782Simp		return (NULL);
892133782Simp	cbb_insert_res(sc, res, type, *rid);
893133782Simp	if (flags & RF_ACTIVE) {
894133782Simp		if (bus_activate_resource(child, type, *rid, res) != 0) {
895133782Simp			bus_release_resource(child, type, *rid, res);
896133782Simp			return (NULL);
897133782Simp		}
898133782Simp	}
899133782Simp
900133782Simp	return (res);
901133782Simp}
902133782Simp
903110841Simpstatic int
904133782Simpexca_release_resource(struct exca_softc *sc, device_t child, int type,
905133782Simp    int rid, struct resource *res)
906133782Simp{
907133782Simp	int error;
908133782Simp
909133782Simp	if (rman_get_flags(res) & RF_ACTIVE) {
910133782Simp		error = bus_deactivate_resource(child, type, rid, res);
911133782Simp		if (error != 0)
912133782Simp			return (error);
913133782Simp	}
914133782Simp	cbb_remove_res(sc, res);
915133782Simp	return (BUS_RELEASE_RESOURCE(device_get_parent(brdev), child,
916133782Simp	    type, rid, res));
917133782Simp}
918133782Simp#endif
919133782Simp
920133782Simpstatic int
921110841Simpexca_modevent(module_t mod, int cmd, void *arg)
92289948Simp{
923110841Simp	return 0;
92489948Simp}
92597613Stakawata
92697613StakawataDEV_MODULE(exca, exca_modevent, NULL);
92797613StakawataMODULE_VERSION(exca, 1);
928