exca.c revision 166742
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 166742 2007-02-15 07:22:27Z 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
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;
182162591Simp	int mem8 = 1 /* mem->kind == PCCARD_A_MEM_ATTR */;
18389948Simp
18489948Simp	map = &mem_map_index[win];
18589948Simp	mem = &sc->mem[win];
186140022Simp	offset = ((mem->cardaddr >> EXCA_CARDMEM_ADDRX_SHIFT) -
187140022Simp	  (mem->addr >> EXCA_SYSMEM_ADDRX_SHIFT)) & 0x3fff;
188110841Simp	exca_putb(sc, map->sysmem_start_lsb,
18989948Simp	    (mem->addr >> EXCA_SYSMEM_ADDRX_SHIFT) & 0xff);
190110841Simp	exca_putb(sc, map->sysmem_start_msb,
19189948Simp	    ((mem->addr >> (EXCA_SYSMEM_ADDRX_SHIFT + 8)) &
192151309Simp	    EXCA_SYSMEM_ADDRX_START_MSB_ADDR_MASK) |
193151309Simp	    (mem8 ? 0 : EXCA_SYSMEM_ADDRX_START_MSB_DATASIZE_16BIT));
19489948Simp
195110841Simp	exca_putb(sc, map->sysmem_stop_lsb,
19689948Simp	    ((mem->addr + mem->realsize - 1) >>
19789948Simp	    EXCA_SYSMEM_ADDRX_SHIFT) & 0xff);
198110841Simp	exca_putb(sc, map->sysmem_stop_msb,
19989948Simp	    (((mem->addr + mem->realsize - 1) >>
20089948Simp	    (EXCA_SYSMEM_ADDRX_SHIFT + 8)) &
201133782Simp	    EXCA_SYSMEM_ADDRX_STOP_MSB_ADDR_MASK) |
202133782Simp	    EXCA_SYSMEM_ADDRX_STOP_MSB_WAIT2);
20389948Simp
204110841Simp	exca_putb(sc, map->sysmem_win,
20589948Simp	    (mem->addr >> EXCA_MEMREG_WIN_SHIFT) & 0xff);
20689948Simp
207140022Simp	exca_putb(sc, map->cardmem_lsb, offset & 0xff);
208140022Simp	exca_putb(sc, map->cardmem_msb, (((offset >> 8) & 0xff) &
20989948Simp	    EXCA_CARDMEM_ADDRX_MSB_ADDR_MASK) |
210119551Simp	    ((mem->kind == PCCARD_A_MEM_ATTR) ?
21189948Simp	    EXCA_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0));
21289948Simp
213119520Simp#ifdef EXCA_DEBUG
214119551Simp	if (mem->kind == PCCARD_A_MEM_ATTR)
215119520Simp		printf("attribtue memory\n");
216119520Simp	else
217119520Simp		printf("common memory\n");
218119520Simp#endif
219133782Simp	exca_setb(sc, EXCA_ADDRWIN_ENABLE, map->memenable |
220133782Simp	    EXCA_ADDRWIN_ENABLE_MEMCS16);
22189948Simp
22289948Simp	DELAY(100);
22389948Simp#ifdef EXCA_DEBUG
22489948Simp	{
22589948Simp		int r1, r2, r3, r4, r5, r6, r7;
226110841Simp		r1 = exca_getb(sc, map->sysmem_start_msb);
227110841Simp		r2 = exca_getb(sc, map->sysmem_start_lsb);
228110841Simp		r3 = exca_getb(sc, map->sysmem_stop_msb);
229110841Simp		r4 = exca_getb(sc, map->sysmem_stop_lsb);
230110841Simp		r5 = exca_getb(sc, map->cardmem_msb);
231110841Simp		r6 = exca_getb(sc, map->cardmem_lsb);
232110841Simp		r7 = exca_getb(sc, map->sysmem_win);
233133782Simp		printf("exca_do_mem_map win %d: %02x%02x %02x%02x "
234133782Simp		    "%02x%02x %02x (%08x+%06x.%06x*%06x)\n",
23589948Simp		    win, r1, r2, r3, r4, r5, r6, r7,
23689948Simp		    mem->addr, mem->size, mem->realsize,
237119520Simp		    mem->cardaddr);
23889948Simp	}
23989948Simp#endif
24089948Simp}
24189948Simp
24289948Simp/*
24389948Simp * public interface to map a resource.  kind is the type of memory to
24489948Simp * map (either common or attribute).  Memory created via this interface
24589948Simp * starts out at card address 0.  Since the only way to set this is
24689948Simp * to set it on a struct resource after it has been mapped, we're safe
24789948Simp * in maping this assumption.  Note that resources can be remapped using
24889948Simp * exca_do_mem_map so that's how the card address can be set later.
24989948Simp */
25089948Simpint
25189948Simpexca_mem_map(struct exca_softc *sc, int kind, struct resource *res)
25289948Simp{
25389948Simp	int win;
25489948Simp
25589948Simp	for (win = 0; win < EXCA_MEM_WINS; win++) {
25689948Simp		if ((sc->memalloc & (1 << win)) == 0) {
25789948Simp			sc->memalloc |= (1 << win);
25889948Simp			break;
25989948Simp		}
26089948Simp	}
26189948Simp	if (win >= EXCA_MEM_WINS)
26289948Simp		return (1);
263120421Simp	if (((rman_get_start(res) >> EXCA_MEMREG_WIN_SHIFT) & 0xff) != 0 &&
264100703Simp	    (sc->flags & EXCA_HAS_MEMREG_WIN) == 0) {
265100703Simp		device_printf(sc->dev, "Does not support mapping above 24M.");
266100703Simp		return (1);
267100703Simp	}
26889948Simp
26989948Simp	sc->mem[win].cardaddr = 0;
27089948Simp	sc->mem[win].memt = rman_get_bustag(res);
27189948Simp	sc->mem[win].memh = rman_get_bushandle(res);
27289948Simp	sc->mem[win].addr = rman_get_start(res);
27389948Simp	sc->mem[win].size = rman_get_end(res) - sc->mem[win].addr + 1;
27489948Simp	sc->mem[win].realsize = sc->mem[win].size + EXCA_MEM_PAGESIZE - 1;
27589948Simp	sc->mem[win].realsize = sc->mem[win].realsize -
27689948Simp	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
27789948Simp	sc->mem[win].kind = kind;
278119520Simp	DPRINTF("exca_mem_map window %d bus %x+%x card addr %x\n",
279119520Simp	    win, sc->mem[win].addr, sc->mem[win].size, sc->mem[win].cardaddr);
28089948Simp	exca_do_mem_map(sc, win);
28189948Simp
28289948Simp	return (0);
28389948Simp}
28489948Simp
28589948Simp/*
28689948Simp * Private helper function.  This turns off a given memory map that is in
28789948Simp * use.  We do this by just clearing the enable bit in the pcic.  If we needed
28889948Simp * to make memory unmapping/mapping pairs faster, we would have to store
28989948Simp * more state information about the pcic and then use that to intelligently
29089948Simp * to the map/unmap.  However, since we don't do that sort of thing often
29189948Simp * (generally just at configure time), it isn't a case worth optimizing.
29289948Simp */
29389948Simpstatic void
29489948Simpexca_mem_unmap(struct exca_softc *sc, int window)
29589948Simp{
29689948Simp	if (window < 0 || window >= EXCA_MEM_WINS)
29789948Simp		panic("exca_mem_unmap: window out of range");
29889948Simp
29989948Simp	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, mem_map_index[window].memenable);
30089948Simp	sc->memalloc &= ~(1 << window);
30189948Simp}
30289948Simp
30389948Simp/*
30489948Simp * Find the map that we're using to hold the resoruce.  This works well
30589948Simp * so long as the client drivers don't do silly things like map the same
30689948Simp * area mutliple times, or map both common and attribute memory at the
30789948Simp * same time.  This latter restriction is a bug.  We likely should just
30889948Simp * store a pointer to the res in the mem[x] data structure.
30989948Simp */
31089948Simpstatic int
31189948Simpexca_mem_findmap(struct exca_softc *sc, struct resource *res)
31289948Simp{
31389948Simp	int win;
31489948Simp
31589948Simp	for (win = 0; win < EXCA_MEM_WINS; win++) {
31689948Simp		if (sc->mem[win].memt == rman_get_bustag(res) &&
31789948Simp		    sc->mem[win].addr == rman_get_start(res) &&
31889948Simp		    sc->mem[win].size == rman_get_size(res))
31989948Simp			return (win);
32089948Simp	}
32189948Simp	return (-1);
32289948Simp}
32389948Simp
32489948Simp/*
32589948Simp * Set the memory flag.  This means that we are setting if the memory
32689948Simp * is coming from attribute memory or from common memory on the card.
32789948Simp * CIS entries are generally in attribute memory (although they can
32889948Simp * reside in common memory).  Generally, this is the only use for attribute
32989948Simp * memory.  However, some cards require their drivers to dance in both
33089948Simp * common and/or attribute memory and this interface (and setting the
33189948Simp * offset interface) exist for such cards.
33289948Simp */
33389948Simpint
33489948Simpexca_mem_set_flags(struct exca_softc *sc, struct resource *res, uint32_t flags)
33589948Simp{
33689948Simp	int win;
33789948Simp
33889948Simp	win = exca_mem_findmap(sc, res);
33989948Simp	if (win < 0) {
34089948Simp		device_printf(sc->dev,
34189948Simp		    "set_res_flags: specified resource not active\n");
34289948Simp		return (ENOENT);
34389948Simp	}
34489948Simp
34589948Simp	sc->mem[win].kind = flags;
34689948Simp	exca_do_mem_map(sc, win);
34789948Simp	return (0);
34889948Simp}
34989948Simp
35089948Simp/*
35189948Simp * Given a resource, go ahead and unmap it if we can find it in the
35289948Simp * resrouce list that's used.
35389948Simp */
35489948Simpint
35589948Simpexca_mem_unmap_res(struct exca_softc *sc, struct resource *res)
35689948Simp{
35789948Simp	int win;
35889948Simp
35989948Simp	win = exca_mem_findmap(sc, res);
36089948Simp	if (win < 0)
36189948Simp		return (ENOENT);
36289948Simp	exca_mem_unmap(sc, win);
36389948Simp	return (0);
36489948Simp}
36589948Simp
36689948Simp/*
36789948Simp * Set the offset of the memory.  We use this for reading the CIS and
368151410Simp * frobbing the pccard's pccard registers (CCR, etc).  Some drivers
369151410Simp * need to access arbitrary attribute and common memory during their
370151410Simp * initialization and operation.
37189948Simp */
37289948Simpint
37389948Simpexca_mem_set_offset(struct exca_softc *sc, struct resource *res,
37489948Simp    uint32_t cardaddr, uint32_t *deltap)
37589948Simp{
37689948Simp	int win;
37789948Simp	uint32_t delta;
37889948Simp
37989948Simp	win = exca_mem_findmap(sc, res);
38089948Simp	if (win < 0) {
38189948Simp		device_printf(sc->dev,
38289948Simp		    "set_memory_offset: specified resource not active\n");
38389948Simp		return (ENOENT);
38489948Simp	}
385119520Simp	sc->mem[win].cardaddr = cardaddr & ~(EXCA_MEM_PAGESIZE - 1);
38689948Simp	delta = cardaddr % EXCA_MEM_PAGESIZE;
38789948Simp	if (deltap)
38889948Simp		*deltap = delta;
38989948Simp	sc->mem[win].realsize = sc->mem[win].size + delta +
39089948Simp	    EXCA_MEM_PAGESIZE - 1;
39189948Simp	sc->mem[win].realsize = sc->mem[win].realsize -
39289948Simp	    (sc->mem[win].realsize % EXCA_MEM_PAGESIZE);
39389948Simp	exca_do_mem_map(sc, win);
39489948Simp	return (0);
39589948Simp}
39689948Simp
39789948Simp
39889948Simp/* I/O */
39989948Simp
40089948Simp#define	EXCA_IOINFO(NUM) {						\
40189948Simp	EXCA_IOADDR ## NUM ## _START_LSB,				\
40289948Simp	EXCA_IOADDR ## NUM ## _START_MSB,				\
40389948Simp	EXCA_IOADDR ## NUM ## _STOP_LSB,				\
40489948Simp	EXCA_IOADDR ## NUM ## _STOP_MSB,				\
40597708Salfred	EXCA_ADDRWIN_ENABLE_IO ## NUM,					\
40689948Simp	EXCA_IOCTL_IO ## NUM ## _WAITSTATE				\
40789948Simp	| EXCA_IOCTL_IO ## NUM ## _ZEROWAIT				\
40889948Simp	| EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_MASK			\
40989948Simp	| EXCA_IOCTL_IO ## NUM ## _DATASIZE_MASK,			\
41089948Simp	{								\
41189948Simp		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_CARD,		\
41289948Simp		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
41389948Simp		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_8BIT,		\
41489948Simp		EXCA_IOCTL_IO ## NUM ## _IOCS16SRC_DATASIZE		\
41589948Simp		| EXCA_IOCTL_IO ## NUM ## _DATASIZE_16BIT,		\
41689948Simp	}								\
41789948Simp}
41889948Simp
41989948Simpstatic struct io_map_index_st {
42089948Simp	int	start_lsb;
42189948Simp	int	start_msb;
42289948Simp	int	stop_lsb;
42389948Simp	int	stop_msb;
42489948Simp	int	ioenable;
42589948Simp	int	ioctlmask;
42689948Simp	int	ioctlbits[3]; /* indexed by PCCARD_WIDTH_* */
42789948Simp} io_map_index[] = {
42889948Simp	EXCA_IOINFO(0),
42989948Simp	EXCA_IOINFO(1),
43089948Simp};
43189948Simp#undef	EXCA_IOINFO
43289948Simp
43389948Simpstatic void
43489948Simpexca_do_io_map(struct exca_softc *sc, int win)
43589948Simp{
43689948Simp	struct io_map_index_st *map;
43789948Simp
43889948Simp	struct pccard_io_handle *io;
43989948Simp
44089948Simp	map = &io_map_index[win];
44189948Simp	io = &sc->io[win];
442110841Simp	exca_putb(sc, map->start_lsb, io->addr & 0xff);
443110841Simp	exca_putb(sc, map->start_msb, (io->addr >> 8) & 0xff);
44489948Simp
445110841Simp	exca_putb(sc, map->stop_lsb, (io->addr + io->size - 1) & 0xff);
446110841Simp	exca_putb(sc, map->stop_msb, ((io->addr + io->size - 1) >> 8) & 0xff);
44789948Simp
44889948Simp	exca_clrb(sc, EXCA_IOCTL, map->ioctlmask);
44989948Simp	exca_setb(sc, EXCA_IOCTL, map->ioctlbits[io->width]);
45089948Simp
45189948Simp	exca_setb(sc, EXCA_ADDRWIN_ENABLE, map->ioenable);
45289948Simp#ifdef EXCA_DEBUG
45389948Simp	{
45489948Simp		int r1, r2, r3, r4;
455110841Simp		r1 = exca_getb(sc, map->start_msb);
456110841Simp		r2 = exca_getb(sc, map->start_lsb);
457110841Simp		r3 = exca_getb(sc, map->stop_msb);
458110841Simp		r4 = exca_getb(sc, map->stop_lsb);
45989948Simp		DPRINTF("exca_do_io_map window %d: %02x%02x %02x%02x "
46089948Simp		    "(%08x+%08x)\n", win, r1, r2, r3, r4,
46189948Simp		    io->addr, io->size);
46289948Simp	}
46389948Simp#endif
46489948Simp}
46589948Simp
46689948Simpint
46789948Simpexca_io_map(struct exca_softc *sc, int width, struct resource *r)
46889948Simp{
46989948Simp	int win;
47089948Simp#ifdef EXCA_DEBUG
47189948Simp	static char *width_names[] = { "auto", "io8", "io16"};
47289948Simp#endif
47389948Simp	for (win=0; win < EXCA_IO_WINS; win++) {
47489948Simp		if ((sc->ioalloc & (1 << win)) == 0) {
47589948Simp			sc->ioalloc |= (1 << win);
47689948Simp			break;
47789948Simp		}
47889948Simp	}
47989948Simp	if (win >= EXCA_IO_WINS)
48089948Simp		return (1);
48189948Simp
48289948Simp	sc->io[win].iot = rman_get_bustag(r);
48389948Simp	sc->io[win].ioh = rman_get_bushandle(r);
48489948Simp	sc->io[win].addr = rman_get_start(r);
48589948Simp	sc->io[win].size = rman_get_end(r) - sc->io[win].addr + 1;
48689948Simp	sc->io[win].flags = 0;
48789948Simp	sc->io[win].width = width;
48889948Simp	DPRINTF("exca_io_map window %d %s port %x+%x\n",
48989948Simp	    win, width_names[width], sc->io[win].addr,
49089948Simp	    sc->io[win].size);
49189948Simp	exca_do_io_map(sc, win);
49289948Simp
49389948Simp	return (0);
49489948Simp}
49589948Simp
49689948Simpstatic void
49789948Simpexca_io_unmap(struct exca_softc *sc, int window)
49889948Simp{
49989948Simp	if (window >= EXCA_IO_WINS)
50089948Simp		panic("exca_io_unmap: window out of range");
50189948Simp
50289948Simp	exca_clrb(sc, EXCA_ADDRWIN_ENABLE, io_map_index[window].ioenable);
50389948Simp
50489948Simp	sc->ioalloc &= ~(1 << window);
50589948Simp
50689948Simp	sc->io[window].iot = 0;
50789948Simp	sc->io[window].ioh = 0;
50889948Simp	sc->io[window].addr = 0;
50989948Simp	sc->io[window].size = 0;
51089948Simp	sc->io[window].flags = 0;
51189948Simp	sc->io[window].width = 0;
51289948Simp}
51389948Simp
51489948Simpstatic int
51589948Simpexca_io_findmap(struct exca_softc *sc, struct resource *res)
51689948Simp{
51789948Simp	int win;
51889948Simp
51989948Simp	for (win = 0; win < EXCA_IO_WINS; win++) {
52089948Simp		if (sc->io[win].iot == rman_get_bustag(res) &&
52189948Simp		    sc->io[win].addr == rman_get_start(res) &&
52289948Simp		    sc->io[win].size == rman_get_size(res))
52389948Simp			return (win);
52489948Simp	}
52589948Simp	return (-1);
52689948Simp}
52789948Simp
52889948Simp
52989948Simpint
53089948Simpexca_io_unmap_res(struct exca_softc *sc, struct resource *res)
53189948Simp{
53289948Simp	int win;
53389948Simp
53489948Simp	win = exca_io_findmap(sc, res);
53589948Simp	if (win < 0)
53689948Simp		return (ENOENT);
53789948Simp	exca_io_unmap(sc, win);
53889948Simp	return (0);
53989948Simp}
54089948Simp
54189948Simp/* Misc */
54289948Simp
54389948Simp/*
54489948Simp * If interrupts are enabled, then we should be able to just wait for
54589948Simp * an interrupt routine to wake us up.  Busy waiting shouldn't be
54689948Simp * necessary.  Sadly, not all legacy ISA cards support an interrupt
54789948Simp * for the busy state transitions, at least according to their datasheets,
54889948Simp * so we busy wait a while here..
54989948Simp */
55089948Simpstatic void
55189948Simpexca_wait_ready(struct exca_softc *sc)
55289948Simp{
55389948Simp	int i;
55489948Simp	DEVPRINTF(sc->dev, "exca_wait_ready: status 0x%02x\n",
555110841Simp	    exca_getb(sc, EXCA_IF_STATUS));
55689948Simp	for (i = 0; i < 10000; i++) {
557110841Simp		if (exca_getb(sc, EXCA_IF_STATUS) & EXCA_IF_STATUS_READY)
55889948Simp			return;
55989948Simp		DELAY(500);
56089948Simp	}
56189948Simp	device_printf(sc->dev, "ready never happened, status = %02x\n",
562110841Simp	    exca_getb(sc, EXCA_IF_STATUS));
56389948Simp}
56489948Simp
56589948Simp/*
56689948Simp * Reset the card.  Ideally, we'd do a lot of this via interrupts.
56789948Simp * However, many PC Cards will deassert the ready signal.  This means
56889948Simp * that they are asserting an interrupt.  This makes it hard to
56989948Simp * do anything but a busy wait here.  One could argue that these
57089948Simp * such cards are broken, or that the bridge that allows this sort
57189948Simp * of interrupt through isn't quite what you'd want (and may be a standards
572150460Simp * violation).  However, such arguing would leave a huge class of PC Cards
573104601Simp * and bridges out of reach for use in the system.
574104601Simp *
575104601Simp * Maybe I should reevaluate the above based on the power bug I fixed
576104601Simp * in OLDCARD.
57789948Simp */
57889948Simpvoid
57989948Simpexca_reset(struct exca_softc *sc, device_t child)
58089948Simp{
58189948Simp	int win;
58289948Simp
58389948Simp	/* enable socket i/o */
58489948Simp	exca_setb(sc, EXCA_PWRCTL, EXCA_PWRCTL_OE);
58589948Simp
586110841Simp	exca_putb(sc, EXCA_INTR, EXCA_INTR_ENABLE);
58789948Simp	/* hold reset for 30ms */
58889948Simp	DELAY(30*1000);
58989948Simp	/* clear the reset flag */
59089948Simp	exca_setb(sc, EXCA_INTR, EXCA_INTR_RESET);
591150460Simp	/* wait 20ms as per PC Card standard (r2.01) section 4.3.6 */
59289948Simp	DELAY(20*1000);
59389948Simp
59489948Simp	exca_wait_ready(sc);
59589948Simp
59689948Simp	/* disable all address windows */
597110841Simp	exca_putb(sc, EXCA_ADDRWIN_ENABLE, 0);
59889948Simp
599120872Simp	exca_setb(sc, EXCA_INTR, EXCA_INTR_CARDTYPE_IO);
600120872Simp	DEVPRINTF(sc->dev, "card type is io\n");
60189948Simp
60289948Simp	/* reinstall all the memory and io mappings */
60389948Simp	for (win = 0; win < EXCA_MEM_WINS; ++win)
60489948Simp		if (sc->memalloc & (1 << win))
60589948Simp			exca_do_mem_map(sc, win);
60689948Simp	for (win = 0; win < EXCA_IO_WINS; ++win)
60789948Simp		if (sc->ioalloc & (1 << win))
60889948Simp			exca_do_io_map(sc, win);
60989948Simp}
61089948Simp
61189948Simp/*
61289948Simp * Initialize the exca_softc data structure for the first time.
61389948Simp */
61489948Simpvoid
615100703Simpexca_init(struct exca_softc *sc, device_t dev,
616100703Simp    bus_space_tag_t bst, bus_space_handle_t bsh, uint32_t offset)
61789948Simp{
61889948Simp	sc->dev = dev;
61989948Simp	sc->memalloc = 0;
62089948Simp	sc->ioalloc = 0;
62189948Simp	sc->bst = bst;
62289948Simp	sc->bsh = bsh;
62389948Simp	sc->offset = offset;
62489948Simp	sc->flags = 0;
625110841Simp	sc->getb = exca_mem_getb;
626110841Simp	sc->putb = exca_mem_putb;
62789948Simp}
62889948Simp
62989948Simp/*
630110841Simp * Is this socket valid?
631110841Simp */
632110841Simpstatic int
633110841Simpexca_valid_slot(struct exca_softc *exca)
634110841Simp{
635110841Simp	uint8_t c;
636110841Simp
637115988Simp	/* Assume the worst */
638115988Simp	exca->chipset = EXCA_BOGUS;
639115988Simp
640110841Simp	/*
641110841Simp	 * see if there's a PCMCIA controller here
642110841Simp	 * Intel PCMCIA controllers use 0x82 and 0x83
643110841Simp	 * IBM clone chips use 0x88 and 0x89, apparently
644110841Simp	 */
645110841Simp	c = exca_getb(exca, EXCA_IDENT);
646151309Simp	DEVPRINTF(exca->dev, "Ident is %x\n", c);
647110841Simp	if ((c & EXCA_IDENT_IFTYPE_MASK) != EXCA_IDENT_IFTYPE_MEM_AND_IO)
648110841Simp		return (0);
649110841Simp	if ((c & EXCA_IDENT_ZERO) != 0)
650110841Simp		return (0);
651110841Simp	switch (c & EXCA_IDENT_REV_MASK) {
652110841Simp	/*
653110841Simp	 *	82365 or clones.
654110841Simp	 */
655110841Simp	case EXCA_IDENT_REV_I82365SLR0:
656110841Simp	case EXCA_IDENT_REV_I82365SLR1:
657110841Simp		exca->chipset = EXCA_I82365;
658110841Simp		/*
659110841Simp		 * Check for Vadem chips by unlocking their extra
660110841Simp		 * registers and looking for valid ID.  Bit 3 in
661110841Simp		 * the ID register is normally 0, except when
662110841Simp		 * EXCA_VADEMREV is set.  Other bridges appear
663110841Simp		 * to ignore this frobbing.
664110841Simp		 */
665110841Simp		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
666110841Simp		    EXCA_VADEM_COOKIE1);
667110841Simp		bus_space_write_1(exca->bst, exca->bsh, EXCA_REG_INDEX,
668110841Simp		    EXCA_VADEM_COOKIE2);
669110841Simp		exca_setb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
670110841Simp		c = exca_getb(exca, EXCA_IDENT);
671110841Simp		if (c & 0x08) {
672110841Simp			switch (c & 7) {
673110841Simp			case 1:
674110841Simp				exca->chipset = EXCA_VG365;
675110841Simp				break;
676110841Simp			case 2:
677110841Simp				exca->chipset = EXCA_VG465;
678110841Simp				break;
679110841Simp			case 3:
680110841Simp				exca->chipset = EXCA_VG468;
681110841Simp				break;
682110841Simp			default:
683110841Simp				exca->chipset = EXCA_VG469;
684110841Simp				break;
685110841Simp			}
686110841Simp			exca_clrb(exca, EXCA_VADEM_VMISC, EXCA_VADEM_REV);
687110841Simp			break;
688110841Simp		}
689110841Simp		/*
690110841Simp		 * Check for RICOH RF5C[23]96 PCMCIA Controller
691110841Simp		 */
692110841Simp		c = exca_getb(exca, EXCA_RICOH_ID);
693110841Simp		if (c == EXCA_RID_396) {
694110841Simp			exca->chipset = EXCA_RF5C396;
695110841Simp			break;
696110841Simp		} else if (c == EXCA_RID_296) {
697110841Simp			exca->chipset = EXCA_RF5C296;
698110841Simp			break;
699110841Simp		}
700110841Simp		/*
701110841Simp		 *	Check for Cirrus logic chips.
702110841Simp		 */
703110841Simp		exca_putb(exca, EXCA_CIRRUS_CHIP_INFO, 0);
704110841Simp		c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
705110841Simp		if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) ==
706110841Simp		    EXCA_CIRRUS_CHIP_INFO_CHIP_ID) {
707110841Simp			c = exca_getb(exca, EXCA_CIRRUS_CHIP_INFO);
708110841Simp			if ((c & EXCA_CIRRUS_CHIP_INFO_CHIP_ID) == 0) {
709110841Simp				if (c & EXCA_CIRRUS_CHIP_INFO_SLOTS)
710110841Simp					exca->chipset = EXCA_PD6722;
711110841Simp				else
712110841Simp					exca->chipset = EXCA_PD6710;
713110841Simp				break;
714110841Simp			}
715110841Simp		}
716110841Simp		break;
717110841Simp
718110841Simp	case EXCA_IDENT_REV_I82365SLDF:
719110841Simp		/*
720110841Simp		 *	Intel i82365sl-DF step or maybe a vlsi 82c146
721110841Simp		 * we detected the vlsi case earlier, so if the controller
722110841Simp		 * isn't set, we know it is a i82365sl step D.
723110841Simp		 */
724110841Simp		exca->chipset = EXCA_I82365SL_DF;
725110841Simp		break;
726110841Simp	case EXCA_IDENT_REV_IBM1:
727110841Simp	case EXCA_IDENT_REV_IBM2:
728110841Simp		exca->chipset = EXCA_IBM;
729110841Simp		break;
730110841Simp	case EXCA_IDENT_REV_IBM_KING:
731110841Simp		exca->chipset = EXCA_IBM_KING;
732110841Simp		break;
733110841Simp	default:
734110841Simp		return (0);
735110841Simp	}
736110841Simp	return (1);
737110841Simp}
738110841Simp
739110841Simp/*
74089948Simp * Probe the expected slots.  We maybe should set the ID for each of these
74189948Simp * slots too while we're at it.  But maybe that belongs to a separate
74289948Simp * function.
74389948Simp *
744110841Simp * The caller must guarantee that at least EXCA_NSLOTS are present in exca.
74589948Simp */
74689948Simpint
747110841Simpexca_probe_slots(device_t dev, struct exca_softc *exca, bus_space_tag_t iot,
748110841Simp    bus_space_handle_t ioh)
74989948Simp{
75089948Simp	int err;
75189948Simp	int i;
75289948Simp
75389948Simp	err = ENXIO;
754110841Simp	for (i = 0; i < EXCA_NSLOTS; i++)  {
755100703Simp		exca_init(&exca[i], dev, iot, ioh, i * EXCA_SOCKET_SIZE);
756110841Simp		exca->getb = exca_io_getb;
757110841Simp		exca->putb = exca_io_putb;
758151458Simp		if (exca_valid_slot(&exca[i])) {
759151458Simp			device_set_desc(dev, chip_names[exca[i].chipset]);
76089948Simp			err = 0;
761151458Simp		}
76289948Simp	}
76389948Simp	return (err);
76489948Simp}
76589948Simp
766115988Simpvoid
767115988Simpexca_insert(struct exca_softc *exca)
768115988Simp{
769115988Simp	if (exca->pccarddev != NULL) {
770115988Simp		if (CARD_ATTACH_CARD(exca->pccarddev) != 0)
771115988Simp			device_printf(exca->dev,
772115988Simp			    "PC Card card activation failed\n");
773115988Simp	} else {
774115988Simp		device_printf(exca->dev,
775115988Simp		    "PC Card inserted, but no pccard bus.\n");
776115988Simp	}
777115988Simp}
778115988Simp
779115988Simp
780115988Simpvoid
781115988Simpexca_removal(struct exca_softc *exca)
782115988Simp{
783166742Simp	if (device_is_attached(exca->pccarddev))
784115988Simp		CARD_DETACH_CARD(exca->pccarddev);
785115988Simp}
786115988Simp
787115988Simpint
788115988Simpexca_activate_resource(struct exca_softc *exca, device_t child, int type,
789115988Simp    int rid, struct resource *res)
790115988Simp{
791115988Simp	int err;
792115988Simp	if (!(rman_get_flags(res) & RF_ACTIVE)) { /* not already activated */
793115988Simp		switch (type) {
794115988Simp		case SYS_RES_IOPORT:
795119551Simp			err = exca_io_map(exca, PCCARD_WIDTH_AUTO, res);
796115988Simp			break;
797115988Simp		case SYS_RES_MEMORY:
798119551Simp			err = exca_mem_map(exca, PCCARD_A_MEM_COM, res);
799115988Simp			break;
800115988Simp		default:
801115988Simp			err = 0;
802115988Simp			break;
803115988Simp		}
804115988Simp		if (err)
805115988Simp			return (err);
806115988Simp
807115988Simp	}
808115988Simp	return (BUS_ACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
809115988Simp		  type, rid, res));
810115988Simp}
811115988Simp
812115988Simpint
813115988Simpexca_deactivate_resource(struct exca_softc *exca, device_t child, int type,
814115988Simp    int rid, struct resource *res)
815115988Simp{
816115988Simp	if (rman_get_flags(res) & RF_ACTIVE) { /* if activated */
817115988Simp		switch (type) {
818115988Simp		case SYS_RES_IOPORT:
819115988Simp			if (exca_io_unmap_res(exca, res))
820115988Simp				return (ENOENT);
821115988Simp			break;
822115988Simp		case SYS_RES_MEMORY:
823115988Simp			if (exca_mem_unmap_res(exca, res))
824115988Simp				return (ENOENT);
825115988Simp			break;
826115988Simp		}
827115988Simp	}
828115988Simp	return (BUS_DEACTIVATE_RESOURCE(device_get_parent(exca->dev), child,
829115988Simp	    type, rid, res));
830115988Simp}
831115988Simp
832133782Simp#if 0
833133782Simpstatic struct resource *
834133782Simpexca_alloc_resource(struct exca_softc *sc, device_t child, int type, int *rid,
835133782Simp    u_long start, u_long end, u_long count, uint flags)
836133782Simp{
837133782Simp	struct resource *res = NULL;
838133782Simp	int tmp;
839133782Simp
840133782Simp	switch (type) {
841133782Simp	case SYS_RES_MEMORY:
842133782Simp		if (start < cbb_start_mem)
843133782Simp			start = cbb_start_mem;
844133782Simp		if (end < start)
845133782Simp			end = start;
846133782Simp		flags = (flags & ~RF_ALIGNMENT_MASK) |
847133782Simp		    rman_make_alignment_flags(CBB_MEMALIGN);
848133782Simp		break;
849133782Simp	case SYS_RES_IOPORT:
850133782Simp		if (start < cbb_start_16_io)
851133782Simp			start = cbb_start_16_io;
852133782Simp		if (end < start)
853133782Simp			end = start;
854133782Simp		break;
855133782Simp	case SYS_RES_IRQ:
856133782Simp		tmp = rman_get_start(sc->irq_res);
857133782Simp		if (start > tmp || end < tmp || count != 1) {
858133782Simp			device_printf(child, "requested interrupt %ld-%ld,"
859133782Simp			    "count = %ld not supported by cbb\n",
860133782Simp			    start, end, count);
861133782Simp			return (NULL);
862133782Simp		}
863133782Simp		flags |= RF_SHAREABLE;
864133782Simp		start = end = rman_get_start(sc->irq_res);
865133782Simp		break;
866133782Simp	}
867133782Simp	res = BUS_ALLOC_RESOURCE(up, child, type, rid,
868133782Simp	    start, end, count, flags & ~RF_ACTIVE);
869133782Simp	if (res == NULL)
870133782Simp		return (NULL);
871133782Simp	cbb_insert_res(sc, res, type, *rid);
872133782Simp	if (flags & RF_ACTIVE) {
873133782Simp		if (bus_activate_resource(child, type, *rid, res) != 0) {
874133782Simp			bus_release_resource(child, type, *rid, res);
875133782Simp			return (NULL);
876133782Simp		}
877133782Simp	}
878133782Simp
879133782Simp	return (res);
880133782Simp}
881133782Simp
882110841Simpstatic int
883133782Simpexca_release_resource(struct exca_softc *sc, device_t child, int type,
884133782Simp    int rid, struct resource *res)
885133782Simp{
886133782Simp	int error;
887133782Simp
888133782Simp	if (rman_get_flags(res) & RF_ACTIVE) {
889133782Simp		error = bus_deactivate_resource(child, type, rid, res);
890133782Simp		if (error != 0)
891133782Simp			return (error);
892133782Simp	}
893133782Simp	cbb_remove_res(sc, res);
894133782Simp	return (BUS_RELEASE_RESOURCE(device_get_parent(brdev), child,
895133782Simp	    type, rid, res));
896133782Simp}
897133782Simp#endif
898133782Simp
899133782Simpstatic int
900110841Simpexca_modevent(module_t mod, int cmd, void *arg)
90189948Simp{
902110841Simp	return 0;
90389948Simp}
90497613Stakawata
90597613StakawataDEV_MODULE(exca, exca_modevent, NULL);
90697613StakawataMODULE_VERSION(exca, 1);
907