xref: /netbsd-src/sys/arch/sparc/dev/ts102.c (revision 1dc652ef5a0bffbd0917f95e0797bad8c6fc8efd)
1f4a919f6Smacallan /*	$OpenBSD: ts102.c,v 1.14 2005/01/27 17:03:23 millert Exp $	*/
2*1dc652efSthorpej /*	$NetBSD: ts102.c,v 1.22 2023/12/20 05:33:18 thorpej Exp $ */
3f4a919f6Smacallan /*
4f4a919f6Smacallan  * Copyright (c) 2003, 2004, Miodrag Vallat.
5edc65871Smacallan  * Copyright (c) 2005, Michael Lorenz.
6f4a919f6Smacallan  *
7f4a919f6Smacallan  * Redistribution and use in source and binary forms, with or without
8f4a919f6Smacallan  * modification, are permitted provided that the following conditions
9f4a919f6Smacallan  * are met:
10f4a919f6Smacallan  * 1. Redistributions of source code must retain the above copyright
11f4a919f6Smacallan  *    notice, this list of conditions and the following disclaimer.
12f4a919f6Smacallan  * 2. Redistributions in binary form must reproduce the above copyright
13f4a919f6Smacallan  *    notice, this list of conditions and the following disclaimer in the
14f4a919f6Smacallan  *    documentation and/or other materials provided with the distribution.
15f4a919f6Smacallan  *
16f4a919f6Smacallan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17f4a919f6Smacallan  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18f4a919f6Smacallan  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19f4a919f6Smacallan  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20f4a919f6Smacallan  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21f4a919f6Smacallan  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22f4a919f6Smacallan  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f4a919f6Smacallan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24f4a919f6Smacallan  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25f4a919f6Smacallan  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26f4a919f6Smacallan  * POSSIBILITY OF SUCH DAMAGE.
27f4a919f6Smacallan  */
28f4a919f6Smacallan 
29f4a919f6Smacallan /*
30f4a919f6Smacallan  * Driver for the PCMCIA controller found in Tadpole SPARCbook 3 series
31f4a919f6Smacallan  * notebooks.
32f4a919f6Smacallan  *
33f4a919f6Smacallan  * Based on the information provided in the SPARCbook 3 Technical Reference
34f4a919f6Smacallan  * Manual (s3gxtrmb.pdf), chapter 7.  A few ramblings against this document
35f4a919f6Smacallan  * and/or the chip itself are scattered across this file.
36f4a919f6Smacallan  *
37f4a919f6Smacallan  * Implementation notes:
38f4a919f6Smacallan  *
39f4a919f6Smacallan  * - The TS102 exports its PCMCIA windows as SBus memory ranges: 64MB for
40f4a919f6Smacallan  *   the common memory window, and 16MB for the attribute and I/O windows.
41f4a919f6Smacallan  *
42f4a919f6Smacallan  *   Mapping the whole windows would consume 192MB of address space, which
43f4a919f6Smacallan  *   is much more that what the iospace can offer.
44f4a919f6Smacallan  *
45f4a919f6Smacallan  *   A best-effort solution would be to map the windows on demand. However,
46f4a919f6Smacallan  *   due to the wap mapdev() works, the va used for the mappings would be
47f4a919f6Smacallan  *   lost after unmapping (although using an extent to register iospace memory
48f4a919f6Smacallan  *   usage would fix this). So, instead, we will do a fixed mapping of a subset
49f4a919f6Smacallan  *   of each window upon attach - this is similar to what the stp4020 driver
50f4a919f6Smacallan  *   does.
51f4a919f6Smacallan  *
52f4a919f6Smacallan  * Endianness farce:
53f4a919f6Smacallan  *
54f4a919f6Smacallan  * - The documentation pretends that the endianness settings only affect the
55f4a919f6Smacallan  *   common memory window. Gee, thanks a lot. What about other windows, then?
56f4a919f6Smacallan  *   As a result, this driver runs with endianness conversions turned off.
57f4a919f6Smacallan  *
58f4a919f6Smacallan  * - One of the little-endian SBus and big-endian PCMCIA flags has the reverse
59f4a919f6Smacallan  *   meaning, actually. To achieve a ``no endianness conversion'' status,
60f4a919f6Smacallan  *   one has to be set and the other unset. It does not matter which one,
61f4a919f6Smacallan  *   though.
62f4a919f6Smacallan  */
63f4a919f6Smacallan 
64f4a919f6Smacallan #include <sys/cdefs.h>
65f4a919f6Smacallan 
66f4a919f6Smacallan #include <sys/param.h>
67f4a919f6Smacallan #include <sys/systm.h>
68f4a919f6Smacallan #include <sys/errno.h>
69f4a919f6Smacallan #include <sys/extent.h>
70f4a919f6Smacallan #include <sys/proc.h>
71f4a919f6Smacallan #include <sys/kernel.h>
72f4a919f6Smacallan #include <sys/kthread.h>
73f4a919f6Smacallan #include <sys/device.h>
74f4a919f6Smacallan 
75f4a919f6Smacallan #include <dev/pcmcia/pcmciareg.h>
76f4a919f6Smacallan #include <dev/pcmcia/pcmciavar.h>
77f4a919f6Smacallan #include <dev/pcmcia/pcmciachip.h>
78f4a919f6Smacallan 
79b6584574Sdyoung #include <sys/bus.h>
80f4a919f6Smacallan #include <machine/intr.h>
81f4a919f6Smacallan #include <machine/autoconf.h>
82f4a919f6Smacallan 
83f4a919f6Smacallan #include <dev/sbus/sbusvar.h>
84f4a919f6Smacallan #include <sparc/dev/ts102reg.h>
85f4a919f6Smacallan 
86f4a919f6Smacallan #include "tctrl.h"
87f4a919f6Smacallan 
88f4a919f6Smacallan #if NTCTRL > 0
89f4a919f6Smacallan #include <machine/tctrl.h>
90f4a919f6Smacallan #include <sparc/dev/tctrlvar.h>
91f4a919f6Smacallan #endif
92f4a919f6Smacallan 
93f4a919f6Smacallan #define	TS102_NUM_SLOTS		2
94f4a919f6Smacallan 
95f4a919f6Smacallan /*
96f4a919f6Smacallan  * Memory ranges
97f4a919f6Smacallan  */
98f4a919f6Smacallan #define	TS102_RANGE_COMMON	0
99f4a919f6Smacallan #define	TS102_RANGE_ATTR	1
100f4a919f6Smacallan #define	TS102_RANGE_IO		2
101f4a919f6Smacallan 
102f4a919f6Smacallan #define	TS102_RANGE_CNT		3
103f4a919f6Smacallan #define	TS102_NUM_RANGES	(TS102_RANGE_CNT * TS102_NUM_SLOTS)
104f4a919f6Smacallan 
105f4a919f6Smacallan #define	TS102_ARBITRARY_MAP_SIZE	(1 * 1024 * 1024)
106f4a919f6Smacallan 
107f4a919f6Smacallan struct	tslot_softc;
108f4a919f6Smacallan 
109f4a919f6Smacallan #ifdef TSLOT_DEBUG
110f4a919f6Smacallan #define TSPRINTF	printf
111f4a919f6Smacallan #else
112f4a919f6Smacallan #define TSPRINTF	while (0) printf
113f4a919f6Smacallan #endif
114f4a919f6Smacallan 
115f4a919f6Smacallan /*
116f4a919f6Smacallan  * Per-slot data
117f4a919f6Smacallan  */
118f4a919f6Smacallan struct	tslot_data {
119f4a919f6Smacallan 	struct tslot_softc	*td_parent;
120266d000aSmacallan 	device_t		td_pcmcia;
121f4a919f6Smacallan 
122648f3c82Smacallan 	volatile uint8_t	*td_regs;
123f4a919f6Smacallan 	bus_addr_t		td_space[TS102_RANGE_CNT];
124f4a919f6Smacallan 	bus_space_tag_t		td_pcmciat;	/* for accessing cards */
125f4a919f6Smacallan 
126f4a919f6Smacallan 	/* Interrupt handler */
127f4a919f6Smacallan 	int			(*td_intr)(void *);
128f4a919f6Smacallan 	void			*td_intrarg;
129f4a919f6Smacallan 	void			*td_softint;
130f4a919f6Smacallan 
131f4a919f6Smacallan 	/* Socket status */
132f4a919f6Smacallan 	int			td_slot;
133f4a919f6Smacallan 	int			td_status;
134f4a919f6Smacallan #define	TS_CARD			0x0001
135f4a919f6Smacallan };
136f4a919f6Smacallan 
137f4a919f6Smacallan struct	tslot_softc {
138266d000aSmacallan 	device_t	sc_dev;
139f4a919f6Smacallan 
140f4a919f6Smacallan 	bus_space_tag_t	sc_bustag;		/* socket control io	*/
141f4a919f6Smacallan 	bus_space_handle_t	sc_regh;	/*  space		*/
142f4a919f6Smacallan 
143f4a919f6Smacallan 	pcmcia_chipset_tag_t sc_pct;
144f4a919f6Smacallan 
14588ab7da9Sad 	lwp_t		*sc_thread;	/* event thread */
146648f3c82Smacallan 	uint32_t	sc_events;	/* sockets with pending events */
147f4a919f6Smacallan 
148f4a919f6Smacallan 	/* bits 0 and 1 are set according to card presence in slot 0 and 1 */
149648f3c82Smacallan 	uint32_t 	sc_active;
150f4a919f6Smacallan 
151f4a919f6Smacallan 	struct tslot_data sc_slot[TS102_NUM_SLOTS];
152f4a919f6Smacallan };
153f4a919f6Smacallan 
154266d000aSmacallan static void tslot_attach(device_t, device_t, void *);
155648f3c82Smacallan static void tslot_event_thread(void *);
156648f3c82Smacallan static int  tslot_intr(void *);
157648f3c82Smacallan static void tslot_intr_disestablish(pcmcia_chipset_handle_t, void *);
158648f3c82Smacallan static void *tslot_intr_establish(pcmcia_chipset_handle_t,
159648f3c82Smacallan     struct pcmcia_function *, int, int (*)(void *), void *);
160648f3c82Smacallan 
161f4a919f6Smacallan const char  *tslot_intr_string(pcmcia_chipset_handle_t, void *);
162648f3c82Smacallan static int  tslot_io_alloc(pcmcia_chipset_handle_t, bus_addr_t, bus_size_t,
163f4a919f6Smacallan     bus_size_t, struct pcmcia_io_handle *);
164648f3c82Smacallan static void tslot_io_free(pcmcia_chipset_handle_t, struct pcmcia_io_handle *);
165648f3c82Smacallan static int  tslot_io_map(pcmcia_chipset_handle_t, int, bus_addr_t, bus_size_t,
166f4a919f6Smacallan     struct pcmcia_io_handle *, int *);
167648f3c82Smacallan static void tslot_io_unmap(pcmcia_chipset_handle_t, int);
168266d000aSmacallan static int  tslot_match(device_t, struct cfdata *, void *);
169648f3c82Smacallan static int  tslot_mem_alloc(pcmcia_chipset_handle_t, bus_size_t,
170f4a919f6Smacallan     struct pcmcia_mem_handle *);
171648f3c82Smacallan static void tslot_mem_free(pcmcia_chipset_handle_t, struct pcmcia_mem_handle *);
172648f3c82Smacallan static int  tslot_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t, bus_size_t,
173f4a919f6Smacallan     struct pcmcia_mem_handle *, bus_size_t *, int *);
174648f3c82Smacallan static void tslot_mem_unmap(pcmcia_chipset_handle_t, int);
175648f3c82Smacallan static int  tslot_print(void *, const char *);
176648f3c82Smacallan static void tslot_queue_event(struct tslot_softc *, int);
177648f3c82Smacallan static void tslot_reset(struct tslot_data *, uint32_t);
178648f3c82Smacallan static void tslot_slot_disable(pcmcia_chipset_handle_t);
179648f3c82Smacallan static void tslot_slot_enable(pcmcia_chipset_handle_t);
180648f3c82Smacallan static void tslot_slot_intr(struct tslot_data *, int);
181648f3c82Smacallan static void tslot_slot_settype(pcmcia_chipset_handle_t, int);
182648f3c82Smacallan static void tslot_update_lcd(struct tslot_softc *, int, int);
183f4a919f6Smacallan static void tslot_intr_dispatch(void *arg);
1846c6277a5Sjdc void tslot_delay(struct tslot_softc *sc, unsigned int ms);
185f4a919f6Smacallan 
186d61452c4Smacallan CFATTACH_DECL_NEW(tslot, sizeof(struct tslot_softc),
187f4a919f6Smacallan     tslot_match, tslot_attach, NULL, NULL);
188f4a919f6Smacallan 
189f4a919f6Smacallan extern struct cfdriver tslot_cd;
190f4a919f6Smacallan 
191f4a919f6Smacallan /*
192f4a919f6Smacallan  * PCMCIA chipset methods
193f4a919f6Smacallan  */
194f4a919f6Smacallan struct	pcmcia_chip_functions tslot_functions = {
195f4a919f6Smacallan 	tslot_mem_alloc,
196f4a919f6Smacallan 	tslot_mem_free,
197f4a919f6Smacallan 	tslot_mem_map,
198f4a919f6Smacallan 	tslot_mem_unmap,
199f4a919f6Smacallan 
200f4a919f6Smacallan 	tslot_io_alloc,
201f4a919f6Smacallan 	tslot_io_free,
202f4a919f6Smacallan 	tslot_io_map,
203f4a919f6Smacallan 	tslot_io_unmap,
204f4a919f6Smacallan 
205f4a919f6Smacallan 	tslot_intr_establish,
206f4a919f6Smacallan 	tslot_intr_disestablish,
207f4a919f6Smacallan 
208f4a919f6Smacallan 	tslot_slot_enable,
209f4a919f6Smacallan 	tslot_slot_disable,
210f4a919f6Smacallan 	tslot_slot_settype
211f4a919f6Smacallan };
212f4a919f6Smacallan 
213648f3c82Smacallan static	uint16_t ts102_read_2(bus_space_tag_t,
214f4a919f6Smacallan 				 bus_space_handle_t,
215f4a919f6Smacallan 				 bus_size_t);
216648f3c82Smacallan static	uint32_t ts102_read_4(bus_space_tag_t,
217f4a919f6Smacallan 				 bus_space_handle_t,
218f4a919f6Smacallan 				 bus_size_t);
219648f3c82Smacallan static	uint64_t ts102_read_8(bus_space_tag_t,
220f4a919f6Smacallan 				 bus_space_handle_t,
221f4a919f6Smacallan 				 bus_size_t);
222f4a919f6Smacallan static	void	ts102_write_2(bus_space_tag_t,
223f4a919f6Smacallan 				bus_space_handle_t,
224f4a919f6Smacallan 				bus_size_t,
225648f3c82Smacallan 				uint16_t);
226f4a919f6Smacallan static	void	ts102_write_4(bus_space_tag_t,
227f4a919f6Smacallan 				bus_space_handle_t,
228f4a919f6Smacallan 				bus_size_t,
229648f3c82Smacallan 				uint32_t);
230f4a919f6Smacallan static	void	ts102_write_8(bus_space_tag_t,
231f4a919f6Smacallan 				bus_space_handle_t,
232f4a919f6Smacallan 				bus_size_t,
233648f3c82Smacallan 				uint64_t);
234f4a919f6Smacallan 
235648f3c82Smacallan static uint16_t
ts102_read_2(bus_space_tag_t space,bus_space_handle_t handle,bus_size_t offset)236648f3c82Smacallan ts102_read_2(bus_space_tag_t space, bus_space_handle_t handle,
237648f3c82Smacallan     bus_size_t offset)
238f4a919f6Smacallan {
239648f3c82Smacallan 	return (le16toh(*(volatile uint16_t *)(handle +
240f4a919f6Smacallan 	    offset)));
241f4a919f6Smacallan }
242f4a919f6Smacallan 
243648f3c82Smacallan static uint32_t
ts102_read_4(bus_space_tag_t space,bus_space_handle_t handle,bus_size_t offset)244648f3c82Smacallan ts102_read_4(bus_space_tag_t space, bus_space_handle_t handle,
245648f3c82Smacallan     bus_size_t offset)
246f4a919f6Smacallan {
247648f3c82Smacallan 	return (le32toh(*(volatile uint32_t *)(handle +
248f4a919f6Smacallan 	    offset)));
249f4a919f6Smacallan }
250f4a919f6Smacallan 
251648f3c82Smacallan static uint64_t
ts102_read_8(bus_space_tag_t space,bus_space_handle_t handle,bus_size_t offset)252648f3c82Smacallan ts102_read_8(bus_space_tag_t space, bus_space_handle_t handle,
253648f3c82Smacallan     bus_size_t offset)
254f4a919f6Smacallan {
255648f3c82Smacallan 	return (le64toh(*(volatile uint64_t *)(handle +
256f4a919f6Smacallan 	    offset)));
257f4a919f6Smacallan }
258f4a919f6Smacallan 
259f4a919f6Smacallan static void
ts102_write_2(bus_space_tag_t space,bus_space_handle_t handle,bus_size_t offset,uint16_t value)260648f3c82Smacallan ts102_write_2(bus_space_tag_t space, bus_space_handle_t handle,
261648f3c82Smacallan     bus_size_t offset, uint16_t value)
262f4a919f6Smacallan {
263648f3c82Smacallan 	(*(volatile uint16_t *)(handle + offset)) =
264f4a919f6Smacallan 	    htole16(value);
265f4a919f6Smacallan }
266f4a919f6Smacallan 
267f4a919f6Smacallan static void
ts102_write_4(bus_space_tag_t space,bus_space_handle_t handle,bus_size_t offset,uint32_t value)268648f3c82Smacallan ts102_write_4(bus_space_tag_t space, bus_space_handle_t handle,
269648f3c82Smacallan     bus_size_t offset, uint32_t value)
270f4a919f6Smacallan {
271648f3c82Smacallan 	(*(volatile uint32_t *)(handle + offset)) =
272f4a919f6Smacallan 	    htole32(value);
273f4a919f6Smacallan }
274f4a919f6Smacallan 
275f4a919f6Smacallan static void
ts102_write_8(bus_space_tag_t space,bus_space_handle_t handle,bus_size_t offset,uint64_t value)276648f3c82Smacallan ts102_write_8(bus_space_tag_t space, bus_space_handle_t handle,
277648f3c82Smacallan     bus_size_t offset, uint64_t value)
278f4a919f6Smacallan {
279648f3c82Smacallan 	(*(volatile uint64_t *)(handle + offset)) =
280f4a919f6Smacallan 	    htole64(value);
281f4a919f6Smacallan }
282f4a919f6Smacallan 
283f4a919f6Smacallan 
284f4a919f6Smacallan #define	TSLOT_READ(slot, offset) \
2856e4f5eb5Suwe 	*(volatile uint16_t *)((slot)->td_regs + (offset))
286f4a919f6Smacallan #define	TSLOT_WRITE(slot, offset, value) \
2876e4f5eb5Suwe 	*(volatile uint16_t *)((slot)->td_regs + (offset)) = (value)
288f4a919f6Smacallan 
289f4a919f6Smacallan /*
290f4a919f6Smacallan  * Attachment and initialization
291f4a919f6Smacallan  */
292f4a919f6Smacallan 
293648f3c82Smacallan static int
tslot_match(device_t parent,struct cfdata * vcf,void * aux)294266d000aSmacallan tslot_match(device_t parent, struct cfdata *vcf, void *aux)
295f4a919f6Smacallan {
296f4a919f6Smacallan 	struct sbus_attach_args *sa = aux;
297f4a919f6Smacallan 
298f4a919f6Smacallan 	return (strcmp("ts102", sa->sa_name) == 0);
299f4a919f6Smacallan }
300f4a919f6Smacallan 
301648f3c82Smacallan static void
tslot_attach(device_t parent,device_t self,void * args)302266d000aSmacallan tslot_attach(device_t parent, device_t self, void *args)
303f4a919f6Smacallan {
304f4a919f6Smacallan 	struct sbus_attach_args *sa = args;
305266d000aSmacallan 	struct tslot_softc *sc = device_private(self);
306f4a919f6Smacallan 	struct tslot_data *td;
3076e4f5eb5Suwe 	volatile uint8_t *regs;
308f4a919f6Smacallan 	int node, slot, rnum, base, size;
309f4a919f6Smacallan 	uint32_t ranges[30];
310f4a919f6Smacallan 	void *rptr = ranges;
311f4a919f6Smacallan 	bus_space_handle_t hrang = 0;
312f4a919f6Smacallan 	bus_space_tag_t tag;
313f4a919f6Smacallan 
314266d000aSmacallan 	sc->sc_dev = self;
315f4a919f6Smacallan 	node = sa->sa_node;
316f4a919f6Smacallan 	sc->sc_bustag=sa->sa_bustag;
317f4a919f6Smacallan 	if (sbus_bus_map(sa->sa_bustag,
318f4a919f6Smacallan 			 sa->sa_slot,
319f4a919f6Smacallan 			 sa->sa_offset,
320f4a919f6Smacallan 			 sa->sa_size,
321f4a919f6Smacallan 			 0, &sc->sc_regh) != 0) {
322cbab9cadSchs 		printf("%s: cannot map registers\n", device_xname(self));
323f4a919f6Smacallan 		return;
324f4a919f6Smacallan 	}
325f4a919f6Smacallan 	regs = (uint8_t *)bus_space_vaddr(sa->sa_bustag, sc->sc_regh);
326f4a919f6Smacallan 
327f4a919f6Smacallan 	tag = bus_space_tag_alloc(sa->sa_bustag, sc);
328f4a919f6Smacallan 	if (tag == NULL) {
329cbab9cadSchs 		printf("%s: attach: out of memory\n", device_xname(self));
330f4a919f6Smacallan 		return;
331f4a919f6Smacallan 	}
332f4a919f6Smacallan 	tag->sparc_read_2 = ts102_read_2;
333f4a919f6Smacallan 	tag->sparc_read_4 = ts102_read_4;
334f4a919f6Smacallan 	tag->sparc_read_8 = ts102_read_8;
335f4a919f6Smacallan 	tag->sparc_write_2 = ts102_write_2;
336f4a919f6Smacallan 	tag->sparc_write_4 = ts102_write_4;
337f4a919f6Smacallan 	tag->sparc_write_8 = ts102_write_8;
338f4a919f6Smacallan 
339f4a919f6Smacallan 	bus_intr_establish(sa->sa_bustag, sa->sa_intr[0].oi_pri,
340f4a919f6Smacallan 	    IPL_NONE, tslot_intr, sc);
341f4a919f6Smacallan 
342f4a919f6Smacallan 	printf(": %d slots\n", TS102_NUM_SLOTS);
343f4a919f6Smacallan 
344f4a919f6Smacallan 	size = sizeof(ranges);
345f4a919f6Smacallan 	if (prom_getprop(node, "ranges", 4, &size, &rptr) != 0) {
346f4a919f6Smacallan 		printf("couldn't read ranges\n");
347f4a919f6Smacallan 		return;
348f4a919f6Smacallan 	}
349f4a919f6Smacallan 
350f4a919f6Smacallan 	/*
351f4a919f6Smacallan 	 * Setup asynchronous event handler
352f4a919f6Smacallan 	 */
353f4a919f6Smacallan 	sc->sc_events = 0;
35488ab7da9Sad 
35588ab7da9Sad 	TSPRINTF("starting event thread...\n");
35688ab7da9Sad 	if (kthread_create(PRI_NONE, 0, NULL, tslot_event_thread, sc,
357d61452c4Smacallan 	    &sc->sc_thread, "%s", device_xname(self)) != 0) {
35888ab7da9Sad 		panic("%s: unable to create event kthread",
359d61452c4Smacallan 		    device_xname(self));
36088ab7da9Sad 	}
361f4a919f6Smacallan 
362f4a919f6Smacallan 	sc->sc_pct = (pcmcia_chipset_tag_t)&tslot_functions;
363f4a919f6Smacallan 	sc->sc_active = 0;
364f4a919f6Smacallan 
365f4a919f6Smacallan 	/*
366f4a919f6Smacallan 	 * Setup slots
367f4a919f6Smacallan 	 */
368f4a919f6Smacallan 	TSPRINTF("mapping resources...\n");
369f4a919f6Smacallan 	for (slot = 0; slot < TS102_NUM_SLOTS; slot++) {
370f4a919f6Smacallan 		td = &sc->sc_slot[slot];
371f4a919f6Smacallan 		TSPRINTF("slot %d, ",slot);
372f4a919f6Smacallan 		for (rnum = 0; rnum < TS102_RANGE_CNT; rnum++) {
373f4a919f6Smacallan 			base = (slot * TS102_RANGE_CNT + rnum) * 5;
374f4a919f6Smacallan 			TSPRINTF("%d: %08x %08x ",rnum,ranges[base + 3],
375f4a919f6Smacallan 			    ranges[base + 4]);
376f4a919f6Smacallan 			if(sbus_bus_map(sc->sc_bustag,
377f4a919f6Smacallan 					sa->sa_slot,
378f4a919f6Smacallan 				 	ranges[base+3],
379f4a919f6Smacallan 				 	TS102_ARBITRARY_MAP_SIZE,
380f4a919f6Smacallan 					0, &hrang) != 0) {
381f4a919f6Smacallan 				printf("%s: cannot map registers\n",
382cbab9cadSchs 				    device_xname(self));
383f4a919f6Smacallan 				return;
384f4a919f6Smacallan 			}
385f4a919f6Smacallan 			TSPRINTF("%08x: %08x ",(uint32_t)ranges[base + 3],
386f4a919f6Smacallan 			    (uint32_t)hrang);
387f4a919f6Smacallan 			td->td_space[rnum] = hrang;
388f4a919f6Smacallan 		}
389f4a919f6Smacallan 		td->td_parent = sc;
390f4a919f6Smacallan 		td->td_pcmciat = tag;
391f4a919f6Smacallan 		td->td_softint = NULL;
392f4a919f6Smacallan 		td->td_regs = regs + slot * (TS102_REG_CARD_B_INT -
393f4a919f6Smacallan 		    TS102_REG_CARD_A_INT);
394f4a919f6Smacallan 		td->td_slot = slot;
395f4a919f6Smacallan 
396f4a919f6Smacallan 		TSPRINTF("resetting slot %d %d\n", slot, (int)td->td_regs);
397f4a919f6Smacallan 		tslot_reset(td, TS102_ARBITRARY_MAP_SIZE);
398f4a919f6Smacallan 	}
399f4a919f6Smacallan }
400f4a919f6Smacallan 
401648f3c82Smacallan static void
tslot_reset(struct tslot_data * td,uint32_t iosize)402648f3c82Smacallan tslot_reset(struct tslot_data *td, uint32_t iosize)
403f4a919f6Smacallan {
404f4a919f6Smacallan 	struct pcmciabus_attach_args paa;
405f4a919f6Smacallan 	int ctl, status;
406f4a919f6Smacallan 
407f4a919f6Smacallan 	paa.paa_busname = "pcmcia";
408f4a919f6Smacallan 	paa.pct = (pcmcia_chipset_tag_t)td->td_parent->sc_pct;
409f4a919f6Smacallan 	paa.pch = (pcmcia_chipset_handle_t)td;
410f4a919f6Smacallan 
4112685996bSthorpej 	td->td_pcmcia = config_found(td->td_parent->sc_dev, &paa, tslot_print,
412c7fb772bSthorpej 	    CFARGS_NONE);
413f4a919f6Smacallan 
414f4a919f6Smacallan 	if (td->td_pcmcia == NULL) {
415f4a919f6Smacallan 		/*
416f4a919f6Smacallan 		 * If no pcmcia attachment, power down the slot.
417f4a919f6Smacallan 		 */
418f4a919f6Smacallan 		tslot_slot_disable((pcmcia_chipset_handle_t)td);
419f4a919f6Smacallan 		return;
420f4a919f6Smacallan 	}
421f4a919f6Smacallan 
422f4a919f6Smacallan 	/*
423f4a919f6Smacallan 	 * Initialize the slot
424f4a919f6Smacallan 	 */
425f4a919f6Smacallan 
426f4a919f6Smacallan 	ctl = TSLOT_READ(td, TS102_REG_CARD_A_CTL);
427f4a919f6Smacallan 
428f4a919f6Smacallan 	/* force low addresses */
429f4a919f6Smacallan 	ctl &= ~(TS102_CARD_CTL_AA_MASK | TS102_CARD_CTL_IA_MASK);
430f4a919f6Smacallan 
431f4a919f6Smacallan 	/* Put SBus and PCMCIA in their respective endian mode */
432648f3c82Smacallan 	ctl |= TS102_CARD_CTL_SBLE;	/* this is not what it looks like! */
433648f3c82Smacallan 	ctl &= ~TS102_CARD_CTL_PCMBE;	/* default */
434f4a919f6Smacallan 
435f4a919f6Smacallan 	/* disable read ahead and address increment */
436f4a919f6Smacallan 	ctl &= ~TS102_CARD_CTL_RAHD;
437f4a919f6Smacallan 	ctl |= TS102_CARD_CTL_INCDIS;
438f4a919f6Smacallan 
439f4a919f6Smacallan 	/* power on */
440f4a919f6Smacallan 	ctl &= ~TS102_CARD_CTL_PWRD;
441f4a919f6Smacallan 	TSLOT_WRITE(td, TS102_REG_CARD_A_CTL, ctl);
442f4a919f6Smacallan 	TSPRINTF("ctl: %x\n", ctl);
443f4a919f6Smacallan 
444f4a919f6Smacallan 	/*
445f4a919f6Smacallan 	 * Enable interrupt upon insertion/removal
446f4a919f6Smacallan 	 */
447f4a919f6Smacallan 
448f4a919f6Smacallan 	TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
449f4a919f6Smacallan 	    TS102_CARD_INT_MASK_CARDDETECT_STATUS);
450f4a919f6Smacallan 
451f4a919f6Smacallan 	status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
452f4a919f6Smacallan 	if (status & TS102_CARD_STS_PRES) {
453f4a919f6Smacallan 		td->td_status = TS_CARD;
454f4a919f6Smacallan 		pcmcia_card_attach(td->td_pcmcia);
455f4a919f6Smacallan 	} else
456f4a919f6Smacallan 		td->td_status = 0;
457f4a919f6Smacallan }
458f4a919f6Smacallan 
459f4a919f6Smacallan /* XXX there ought to be a common function for this... */
460648f3c82Smacallan static int
tslot_print(void * aux,const char * description)461f4a919f6Smacallan tslot_print(void *aux, const char *description)
462f4a919f6Smacallan {
463f4a919f6Smacallan 	struct pcmciabus_attach_args *paa = aux;
464f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)paa->pch;
465f4a919f6Smacallan 
466f4a919f6Smacallan 	printf(" socket %d", td->td_slot);
467f4a919f6Smacallan 	return (UNCONF);
468f4a919f6Smacallan }
469f4a919f6Smacallan 
470f4a919f6Smacallan /*
471f4a919f6Smacallan  * PCMCIA Helpers
472f4a919f6Smacallan  */
473f4a919f6Smacallan 
474648f3c82Smacallan static int
tslot_io_alloc(pcmcia_chipset_handle_t pch,bus_addr_t start,bus_size_t size,bus_size_t align,struct pcmcia_io_handle * pih)475f4a919f6Smacallan tslot_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start, bus_size_t size,
476f4a919f6Smacallan     bus_size_t align, struct pcmcia_io_handle *pih)
477f4a919f6Smacallan {
478f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
479f4a919f6Smacallan 
480f4a919f6Smacallan #ifdef TSLOT_DEBUG
481f4a919f6Smacallan 	printf("[io alloc %x]", (uint32_t)size);
482f4a919f6Smacallan #endif
483f4a919f6Smacallan 
484f4a919f6Smacallan 	pih->iot = td->td_pcmciat;
485f4a919f6Smacallan 	pih->ioh = td->td_space[TS102_RANGE_IO];
486f4a919f6Smacallan 	pih->addr = start;
487f4a919f6Smacallan 	pih->size = size;
488f4a919f6Smacallan 	pih->flags = 0;
489f4a919f6Smacallan 
490f4a919f6Smacallan 	return (0);
491f4a919f6Smacallan }
492f4a919f6Smacallan 
493648f3c82Smacallan static void
tslot_io_free(pcmcia_chipset_handle_t pch,struct pcmcia_io_handle * pih)494f4a919f6Smacallan tslot_io_free(pcmcia_chipset_handle_t pch, struct pcmcia_io_handle *pih)
495f4a919f6Smacallan {
496f4a919f6Smacallan #ifdef TSLOT_DEBUG
497f4a919f6Smacallan 	printf("[io free]");
498f4a919f6Smacallan #endif
499f4a919f6Smacallan }
500f4a919f6Smacallan 
501648f3c82Smacallan static int
tslot_io_map(pcmcia_chipset_handle_t pch,int width,bus_addr_t offset,bus_size_t size,struct pcmcia_io_handle * pih,int * windowp)502f4a919f6Smacallan tslot_io_map(pcmcia_chipset_handle_t pch, int width, bus_addr_t offset,
503f4a919f6Smacallan     bus_size_t size, struct pcmcia_io_handle *pih, int *windowp)
504f4a919f6Smacallan {
505f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
506f4a919f6Smacallan 
507f4a919f6Smacallan #ifdef TSLOT_DEBUG
508f4a919f6Smacallan 	printf("[io map %x/%x", (uint32_t)offset, (uint32_t)size);
509f4a919f6Smacallan #endif
510f4a919f6Smacallan 
511f4a919f6Smacallan 	pih->iot = td->td_pcmciat;
512f4a919f6Smacallan 	if (bus_space_subregion(pih->iot, td->td_space[TS102_RANGE_IO],
513f4a919f6Smacallan 	    offset, size, &pih->ioh) != 0)
514f4a919f6Smacallan 		printf("io_map failed, offset %x\n", (uint32_t)offset);
515f4a919f6Smacallan 	*windowp = 0; /* TS102_RANGE_IO */
516f4a919f6Smacallan 
517f4a919f6Smacallan #ifdef TSLOT_DEBUG
518f4a919f6Smacallan 	printf("->%x/%x]", (uint32_t)pih->ioh, (uint32_t)size);
519f4a919f6Smacallan 	{
520f4a919f6Smacallan 		int addr, line;
521f4a919f6Smacallan 		for( addr = offset; addr < (offset + size); addr += 16) {
522f4a919f6Smacallan 			printf("%04x:", addr);
523f4a919f6Smacallan 			for(line = addr; line < (addr + 16); line += 2) {
524f4a919f6Smacallan 				printf(" %04x", bus_space_read_2(pih->iot,
525f4a919f6Smacallan 				    pih->ioh, line));
526f4a919f6Smacallan 			}
527f4a919f6Smacallan 			printf("\n");
528f4a919f6Smacallan 		}
529f4a919f6Smacallan 	}
530f4a919f6Smacallan #endif
531f4a919f6Smacallan 
532f4a919f6Smacallan 	return (0);
533f4a919f6Smacallan }
534f4a919f6Smacallan 
535648f3c82Smacallan static void
tslot_io_unmap(pcmcia_chipset_handle_t pch,int win)536f4a919f6Smacallan tslot_io_unmap(pcmcia_chipset_handle_t pch, int win)
537f4a919f6Smacallan {
538f4a919f6Smacallan #ifdef TSLOT_DEBUG
539f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
540648f3c82Smacallan 
541f4a919f6Smacallan 	printf("[io unmap]");
542f4a919f6Smacallan 	{
543f4a919f6Smacallan 		int addr, line, offset = 0, size = 0x80;
544f4a919f6Smacallan 		for (addr = offset; addr < (offset + size); addr += 16) {
545f4a919f6Smacallan 			printf("%04x:", addr);
546f4a919f6Smacallan 			for (line = addr; line < (addr + 16); line += 2){
547f4a919f6Smacallan 				printf(" %04x", bus_space_read_2(td->td_pcmciat,
548f4a919f6Smacallan 				    td->td_space[2], line));
549f4a919f6Smacallan 			}
550f4a919f6Smacallan 			printf("\n");
551f4a919f6Smacallan 		}
552f4a919f6Smacallan 	}
553f4a919f6Smacallan #endif
554f4a919f6Smacallan }
555f4a919f6Smacallan 
556648f3c82Smacallan static int
tslot_mem_alloc(pcmcia_chipset_handle_t pch,bus_size_t size,struct pcmcia_mem_handle * pmh)557f4a919f6Smacallan tslot_mem_alloc(pcmcia_chipset_handle_t pch, bus_size_t size,
558f4a919f6Smacallan     struct pcmcia_mem_handle *pmh)
559f4a919f6Smacallan {
560f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
561f4a919f6Smacallan 
562f4a919f6Smacallan #ifdef TSLOT_DEBUG
563f4a919f6Smacallan 	printf("[mem alloc %x]", (uint32_t)size);
564f4a919f6Smacallan #endif
565f4a919f6Smacallan 	pmh->memt = td->td_pcmciat;
566648f3c82Smacallan 	pmh->size = size;
567f4a919f6Smacallan 	pmh->addr = 0;
568f4a919f6Smacallan 	pmh->mhandle = 0;
569f4a919f6Smacallan 	pmh->realsize = size;	/* nothing so far! */
570f4a919f6Smacallan 
571f4a919f6Smacallan 	return (0);
572f4a919f6Smacallan }
573f4a919f6Smacallan 
574648f3c82Smacallan static void
tslot_mem_free(pcmcia_chipset_handle_t pch,struct pcmcia_mem_handle * pmh)575f4a919f6Smacallan tslot_mem_free(pcmcia_chipset_handle_t pch, struct pcmcia_mem_handle *pmh)
576f4a919f6Smacallan {
577f4a919f6Smacallan #ifdef TSLOT_DEBUG
578f4a919f6Smacallan 	printf("[mem free]");
579f4a919f6Smacallan #endif
580f4a919f6Smacallan }
581f4a919f6Smacallan 
582648f3c82Smacallan static int
tslot_mem_map(pcmcia_chipset_handle_t pch,int kind,bus_addr_t addr,bus_size_t size,struct pcmcia_mem_handle * pmh,bus_size_t * offsetp,int * windowp)583f4a919f6Smacallan tslot_mem_map(pcmcia_chipset_handle_t pch, int kind, bus_addr_t addr,
584f4a919f6Smacallan     bus_size_t size, struct pcmcia_mem_handle *pmh, bus_size_t *offsetp,
585f4a919f6Smacallan     int *windowp)
586f4a919f6Smacallan {
587f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
588f4a919f6Smacallan 	int slot;
589f4a919f6Smacallan 
590f4a919f6Smacallan 	slot = kind & PCMCIA_MEM_ATTR ? TS102_RANGE_ATTR : TS102_RANGE_COMMON;
591f4a919f6Smacallan #ifdef TSLOT_DEBUG
592f4a919f6Smacallan 	printf("[mem map %d %x/%x", slot, (uint32_t)addr, (uint32_t)size);
593f4a919f6Smacallan #endif
594f4a919f6Smacallan 
595f4a919f6Smacallan 	pmh->memt = td->td_parent->sc_bustag;
596f4a919f6Smacallan 	if (bus_space_subregion(pmh->memt, td->td_space[slot],
597f4a919f6Smacallan 	    addr, size, &pmh->memh) != 0)
598f4a919f6Smacallan 		printf("mem_map failed, offset %x\n", (uint32_t)addr);
599f4a919f6Smacallan 	pmh->realsize = TS102_ARBITRARY_MAP_SIZE - addr;
600f4a919f6Smacallan 	pmh->size = size;
601f4a919f6Smacallan 	*offsetp = 0;
602f4a919f6Smacallan 	*windowp = 0;
603f4a919f6Smacallan 
604f4a919f6Smacallan #ifdef TSLOT_DEBUG
605f4a919f6Smacallan 	printf("->%x/%x]", (uint32_t)pmh->memh, (uint32_t)size);
606f4a919f6Smacallan #endif
607f4a919f6Smacallan 
608f4a919f6Smacallan 	return (0);
609f4a919f6Smacallan }
610f4a919f6Smacallan 
611648f3c82Smacallan static void
tslot_mem_unmap(pcmcia_chipset_handle_t pch,int win)612f4a919f6Smacallan tslot_mem_unmap(pcmcia_chipset_handle_t pch, int win)
613f4a919f6Smacallan {
614f4a919f6Smacallan #ifdef TSLOT_DEBUG
615f4a919f6Smacallan 	printf("[mem unmap %d]", win);
616f4a919f6Smacallan #endif
617f4a919f6Smacallan }
618f4a919f6Smacallan 
619648f3c82Smacallan static void
tslot_slot_disable(pcmcia_chipset_handle_t pch)620f4a919f6Smacallan tslot_slot_disable(pcmcia_chipset_handle_t pch)
621f4a919f6Smacallan {
622f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
6236c6277a5Sjdc 	int status;
6246c6277a5Sjdc 
625f4a919f6Smacallan #ifdef TSLOT_DEBUG
626f4a919f6Smacallan 	printf("%s: disable slot %d\n",
627cbab9cadSchs 	    device_xname(td->td_parent->sc_dev), td->td_slot);
628f4a919f6Smacallan #endif
629f4a919f6Smacallan 
6306c6277a5Sjdc 	status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
6316c6277a5Sjdc 
6326c6277a5Sjdc 	status &= ~TS102_CARD_STS_ACEN;
633f4a919f6Smacallan 
634f4a919f6Smacallan 	/*
635f4a919f6Smacallan 	 * Disable interrupts, except for insertion.
636f4a919f6Smacallan 	 */
637f4a919f6Smacallan 	TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
638f4a919f6Smacallan 	    TS102_CARD_INT_MASK_CARDDETECT_STATUS);
6396c6277a5Sjdc 
6406c6277a5Sjdc 	/*
6416c6277a5Sjdc 	 * Power down the socket and disable access
6426c6277a5Sjdc 	 */
6436c6277a5Sjdc 	status &= ~TS102_CARD_STS_ACEN;
6446c6277a5Sjdc 	status &= ~(TS102_CARD_STS_VPP1_MASK | TS102_CARD_STS_VPP2_MASK);
6456c6277a5Sjdc 	status |= TS102_CARD_STS_VCCEN;
6466c6277a5Sjdc 	TSLOT_WRITE(td, TS102_REG_CARD_A_STS, status);
6476c6277a5Sjdc 
6486c6277a5Sjdc 	/*
6496c6277a5Sjdc 	 * wait 300ms until power fails (Tpf).
6506c6277a5Sjdc 	 */
6516c6277a5Sjdc 	tslot_delay(td->td_parent, 300);
652f4a919f6Smacallan }
653f4a919f6Smacallan 
654648f3c82Smacallan static void
tslot_slot_enable(pcmcia_chipset_handle_t pch)655f4a919f6Smacallan tslot_slot_enable(pcmcia_chipset_handle_t pch)
656f4a919f6Smacallan {
657f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
658f4a919f6Smacallan 	int status, intr, i;
659f4a919f6Smacallan 
660f4a919f6Smacallan #ifdef TSLOT_DEBUG
661f4a919f6Smacallan 	printf("%s: enable slot %d\n",
662cbab9cadSchs 	    device_xname(td->td_parent->sc_dev), td->td_slot);
663f4a919f6Smacallan #endif
664f4a919f6Smacallan 
665f4a919f6Smacallan 	/* Power down the socket to reset it */
666f4a919f6Smacallan 	status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
667f4a919f6Smacallan 	TSPRINTF("status: %x\n", status);
6686c6277a5Sjdc 
6696c6277a5Sjdc 	status &= ~TS102_CARD_STS_ACEN;
6706c6277a5Sjdc 	status &= ~(TS102_CARD_STS_VPP1_MASK | TS102_CARD_STS_VPP2_MASK);
6716c6277a5Sjdc 	status |= TS102_CARD_STS_VCCEN;
6726c6277a5Sjdc 	TSLOT_WRITE(td, TS102_REG_CARD_A_STS, status);
673f4a919f6Smacallan 
674f4a919f6Smacallan 	/*
675f4a919f6Smacallan 	 * wait 300ms until power fails (Tpf).  Then, wait 100ms since we
676f4a919f6Smacallan 	 * are changing Vcc (Toff).
677f4a919f6Smacallan 	 */
6786c6277a5Sjdc 	tslot_delay(td->td_parent, 300 + 100);
679f4a919f6Smacallan 
680f4a919f6Smacallan 	/*
681f4a919f6Smacallan 	 * Power on the card if not already done, and enable card access
682f4a919f6Smacallan 	 */
683f4a919f6Smacallan 	status |= TS102_CARD_STS_ACEN;
6846c6277a5Sjdc 	status |= TS102_CARD_STS_VPP1_VCC;
685f4a919f6Smacallan 	status &= ~TS102_CARD_STS_VCCEN;
686f4a919f6Smacallan 	TSLOT_WRITE(td, TS102_REG_CARD_A_STS, status);
687f4a919f6Smacallan 
688f4a919f6Smacallan 	/*
689f4a919f6Smacallan 	 * wait 100ms until power raise (Tpr) and 20ms to become
690f4a919f6Smacallan 	 * stable (Tsu(Vcc)).
691f4a919f6Smacallan 	 */
6926c6277a5Sjdc 	tslot_delay(td->td_parent, 100 + 20);
693f4a919f6Smacallan 
694f4a919f6Smacallan 	/*
695f4a919f6Smacallan 	 * hold RESET at least 20us.
696f4a919f6Smacallan 	 */
697f4a919f6Smacallan 	intr = TSLOT_READ(td, TS102_REG_CARD_A_INT);
6986c6277a5Sjdc 	delay(20);
6996c6277a5Sjdc 	TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
7006c6277a5Sjdc 	    intr & ~TS102_CARD_INT_SOFT_RESET);
701f4a919f6Smacallan 
702f4a919f6Smacallan 	/* wait 20ms as per pc card standard (r2.01) section 4.3.6 */
7036c6277a5Sjdc 	tslot_delay(td->td_parent, 20);
704f4a919f6Smacallan 
705f4a919f6Smacallan 	/* We need level-triggered interrupts for PC Card hardware */
706f4a919f6Smacallan 	TSLOT_WRITE(td, TS102_REG_CARD_A_STS,
707f4a919f6Smacallan 		TSLOT_READ(td, TS102_REG_CARD_A_STS) | TS102_CARD_STS_LVL);
708f4a919f6Smacallan 
709f4a919f6Smacallan 	/*
710f4a919f6Smacallan 	 * Wait until the card is unbusy. If it is still busy after 3 seconds,
711f4a919f6Smacallan 	 * give up. We could enable card interrupts and wait for the interrupt
712f4a919f6Smacallan 	 * to happen when BUSY is released, but the interrupt could also be
713f4a919f6Smacallan 	 * triggered by the card itself if it's an I/O card, so better poll
714f4a919f6Smacallan 	 * here.
715f4a919f6Smacallan 	 */
716f4a919f6Smacallan 	for (i = 30000; i != 0; i--) {
717f4a919f6Smacallan 		status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
718f4a919f6Smacallan 		/* If the card has been removed, abort */
719f4a919f6Smacallan 		if ((status & TS102_CARD_STS_PRES) == 0) {
720f4a919f6Smacallan 			tslot_slot_disable(pch);
721f4a919f6Smacallan 			return;
722f4a919f6Smacallan 		}
723f4a919f6Smacallan 		if (status & TS102_CARD_STS_RDY)
724f4a919f6Smacallan 			break;
725f4a919f6Smacallan 		else
7266c6277a5Sjdc 			delay(100);
727f4a919f6Smacallan 	}
728f4a919f6Smacallan 
729f4a919f6Smacallan 	if (i == 0) {
730f4a919f6Smacallan 		printf("%s: slot %d still busy after 3 seconds, status 0x%x\n",
731266d000aSmacallan 		    device_xname(td->td_parent->sc_dev), td->td_slot,
732f4a919f6Smacallan 		    TSLOT_READ(td, TS102_REG_CARD_A_STS));
733f4a919f6Smacallan 		return;
734f4a919f6Smacallan 	}
735f4a919f6Smacallan }
736648f3c82Smacallan static void
tslot_event_thread(void * v)737f4a919f6Smacallan tslot_event_thread(void *v)
738f4a919f6Smacallan {
739f4a919f6Smacallan 	struct tslot_softc *sc = v;
740f4a919f6Smacallan 	struct tslot_data *td;
7412d61a640Smacallan 	int s, status;
742f4a919f6Smacallan 	unsigned int socket;
743648f3c82Smacallan 
7442d61a640Smacallan #if NTCTRL > 0
7452d61a640Smacallan 	int i;
746f4a919f6Smacallan 
747f4a919f6Smacallan 	/*
748f4a919f6Smacallan 	 * First-time setup of our LCD symbol. When a card is present at boot
749f4a919f6Smacallan 	 * time we won't detect a change here and therefore the LCD symbol won't
750f4a919f6Smacallan 	 * light up.
751f4a919f6Smacallan 	 */
752f4a919f6Smacallan 	for (i = 0; i < TS102_NUM_SLOTS; i++) {
753f4a919f6Smacallan 		td = &sc->sc_slot[i];
754f4a919f6Smacallan 		status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
755f4a919f6Smacallan 		tslot_update_lcd(sc, i, status & TS102_CARD_STS_PRES);
756f4a919f6Smacallan 	}
757f4a919f6Smacallan #endif
758f4a919f6Smacallan 
759f4a919f6Smacallan 	for (;;) {
760f4a919f6Smacallan 		s = splhigh();
761f4a919f6Smacallan 
762f4a919f6Smacallan 		if ((socket = ffs(sc->sc_events)) == 0) {
763f4a919f6Smacallan 			splx(s);
7647d93c08cSmacallan 			tsleep(&sc->sc_events, PWAIT, "tslot_event", hz * 30);
765f4a919f6Smacallan 			continue;
766f4a919f6Smacallan 		}
767f4a919f6Smacallan 		socket--;
768f4a919f6Smacallan 		sc->sc_events &= ~(1 << socket);
769f4a919f6Smacallan 		splx(s);
770f4a919f6Smacallan 
771f4a919f6Smacallan 		if (socket >= TS102_NUM_SLOTS) {
772f4a919f6Smacallan #ifdef DEBUG
773f4a919f6Smacallan 			printf("%s: invalid slot number %d\n",
774266d000aSmacallan 			    device_xname(sc->sc_dev), socket);
775f4a919f6Smacallan #endif
776f4a919f6Smacallan 			continue;
777f4a919f6Smacallan 		}
778f4a919f6Smacallan 
779f4a919f6Smacallan 		td = &sc->sc_slot[socket];
780f4a919f6Smacallan 		status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
781f4a919f6Smacallan 
782f4a919f6Smacallan 		if (status & TS102_CARD_STS_PRES) {
783f4a919f6Smacallan 			/* Card insertion */
784f4a919f6Smacallan 			if ((td->td_status & TS_CARD) == 0) {
785f4a919f6Smacallan 				td->td_status |= TS_CARD;
786f4a919f6Smacallan 				tslot_update_lcd(sc, socket, 1);
787f4a919f6Smacallan 				pcmcia_card_attach(td->td_pcmcia);
788f4a919f6Smacallan 			}
789f4a919f6Smacallan 		} else {
790f4a919f6Smacallan 			/* Card removal */
791f4a919f6Smacallan 			if ((td->td_status & TS_CARD) != 0) {
792f4a919f6Smacallan 				tslot_update_lcd(sc, socket, 0);
793f4a919f6Smacallan 				td->td_status &= ~TS_CARD;
794f4a919f6Smacallan 				pcmcia_card_detach(td->td_pcmcia,
795f4a919f6Smacallan 				    DETACH_FORCE);
796f4a919f6Smacallan 			}
797f4a919f6Smacallan 		}
798f4a919f6Smacallan 	}
799f4a919f6Smacallan }
800f4a919f6Smacallan 
801f4a919f6Smacallan /*
802f4a919f6Smacallan  * Interrupt handling
803f4a919f6Smacallan  */
804f4a919f6Smacallan 
805648f3c82Smacallan static int
tslot_intr(void * v)806f4a919f6Smacallan tslot_intr(void *v)
807f4a919f6Smacallan {
808f4a919f6Smacallan 	struct tslot_softc *sc = v;
809f4a919f6Smacallan 	struct tslot_data *td;
810f4a919f6Smacallan 	int intregs[TS102_NUM_SLOTS], *intreg;
811f4a919f6Smacallan 	int i, s, rc = 0;
812f4a919f6Smacallan 
813f4a919f6Smacallan 	s = splhigh();
814f4a919f6Smacallan 
815f4a919f6Smacallan 	/*
816f4a919f6Smacallan 	 * Scan slots, and acknowledge the interrupt if necessary first
817f4a919f6Smacallan 	 */
818f4a919f6Smacallan 	for (i = 0; i < TS102_NUM_SLOTS; i++) {
819f4a919f6Smacallan 		td = &sc->sc_slot[i];
820f4a919f6Smacallan 		intreg = &intregs[i];
821f4a919f6Smacallan 		*intreg = TSLOT_READ(td, TS102_REG_CARD_A_INT);
822f4a919f6Smacallan 
823f4a919f6Smacallan 		/*
824f4a919f6Smacallan 		 * Acknowledge all interrupt situations at once, even if they
825f4a919f6Smacallan 		 * did not occur.
826f4a919f6Smacallan 		 */
827f4a919f6Smacallan 		if ((*intreg & (TS102_CARD_INT_STATUS_IRQ |
828f4a919f6Smacallan 		    TS102_CARD_INT_STATUS_WP_STATUS_CHANGED |
829f4a919f6Smacallan 		    TS102_CARD_INT_STATUS_BATTERY_STATUS_CHANGED |
830f4a919f6Smacallan 		    TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED)) != 0) {
831f4a919f6Smacallan 			rc = 1;
832f4a919f6Smacallan 			TSLOT_WRITE(td, TS102_REG_CARD_A_INT, *intreg |
833f4a919f6Smacallan 			    TS102_CARD_INT_RQST_IRQ |
834f4a919f6Smacallan 			    TS102_CARD_INT_RQST_WP_STATUS_CHANGED |
835f4a919f6Smacallan 			    TS102_CARD_INT_RQST_BATTERY_STATUS_CHANGED |
836f4a919f6Smacallan 			    TS102_CARD_INT_RQST_CARDDETECT_STATUS_CHANGED);
837f4a919f6Smacallan 		}
838f4a919f6Smacallan 	}
839f4a919f6Smacallan 
840f4a919f6Smacallan #ifdef TSLOT_DEBUG
841f4a919f6Smacallan 	printf("tslot_intr: %x %x\n", intregs[0], intregs[1]);
842f4a919f6Smacallan #endif
843f4a919f6Smacallan 
844f4a919f6Smacallan 	/*
845f4a919f6Smacallan 	 * Invoke the interrupt handler for each slot
846f4a919f6Smacallan 	 */
847f4a919f6Smacallan 	for (i = 0; i < TS102_NUM_SLOTS; i++) {
848f4a919f6Smacallan 		td = &sc->sc_slot[i];
849f4a919f6Smacallan 		intreg = &intregs[i];
850f4a919f6Smacallan 
851f4a919f6Smacallan 		if ((*intreg & (TS102_CARD_INT_STATUS_IRQ |
852f4a919f6Smacallan 		    TS102_CARD_INT_STATUS_WP_STATUS_CHANGED |
853f4a919f6Smacallan 		    TS102_CARD_INT_STATUS_BATTERY_STATUS_CHANGED |
854f4a919f6Smacallan 		    TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED)) != 0)
855f4a919f6Smacallan 			tslot_slot_intr(td, *intreg);
856f4a919f6Smacallan 	}
857f4a919f6Smacallan 	splx(s);
858f4a919f6Smacallan 
859f4a919f6Smacallan 	return (rc);
860f4a919f6Smacallan }
861f4a919f6Smacallan 
862648f3c82Smacallan static void
tslot_queue_event(struct tslot_softc * sc,int slot)863f4a919f6Smacallan tslot_queue_event(struct tslot_softc *sc, int slot)
864f4a919f6Smacallan {
865f4a919f6Smacallan 	int s;
866f4a919f6Smacallan 
867f4a919f6Smacallan 	s = splhigh();
868f4a919f6Smacallan 	sc->sc_events |= (1 << slot);
869f4a919f6Smacallan 	splx(s);
870f4a919f6Smacallan 	wakeup(&sc->sc_events);
871f4a919f6Smacallan }
872f4a919f6Smacallan 
873648f3c82Smacallan static void
tslot_slot_intr(struct tslot_data * td,int intreg)874f4a919f6Smacallan tslot_slot_intr(struct tslot_data *td, int intreg)
875f4a919f6Smacallan {
876f4a919f6Smacallan 	struct tslot_softc *sc = td->td_parent;
877f4a919f6Smacallan 	int status, sockstat;
878f4a919f6Smacallan 	uint32_t ireg;
879f4a919f6Smacallan 
880f4a919f6Smacallan 	status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
881f4a919f6Smacallan #ifdef TSLOT_DEBUG
882f4a919f6Smacallan 	printf("%s: interrupt on socket %d ir %x sts %x\n",
883266d000aSmacallan 	    device_xname(sc->sc_dev), td->td_slot, intreg, status);
884fa33d35fSmrg #else
885fa33d35fSmrg 	__USE(status);
886f4a919f6Smacallan #endif
887f4a919f6Smacallan 
888f4a919f6Smacallan 	sockstat = td->td_status;
889f4a919f6Smacallan 
890f4a919f6Smacallan 	/*
891f4a919f6Smacallan 	 * The TS102 queues interrupt request, and may trigger an interrupt
892f4a919f6Smacallan 	 * for a condition the driver does not want to receive anymore (for
893f4a919f6Smacallan 	 * example, after a card gets removed).
894f4a919f6Smacallan 	 * Thus, only proceed if the driver is currently allowing a particular
895f4a919f6Smacallan 	 * condition.
896f4a919f6Smacallan 	 */
897f4a919f6Smacallan 
898f4a919f6Smacallan 	if ((intreg & TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED) != 0 &&
899f4a919f6Smacallan 	    (intreg & TS102_CARD_INT_MASK_CARDDETECT_STATUS) != 0) {
900f4a919f6Smacallan 		tslot_queue_event(sc, td->td_slot);
901f4a919f6Smacallan #ifdef TSLOT_DEBUG
902f4a919f6Smacallan 		printf("%s: slot %d status changed from %d to %d\n",
903266d000aSmacallan 		    device_xname(sc->sc_dev), td->td_slot, sockstat,
904266d000aSmacallan 		    td->td_status);
905f4a919f6Smacallan #endif
906f4a919f6Smacallan 		/*
907f4a919f6Smacallan 		 * Ignore extra interrupt bits, they are part of the change.
908f4a919f6Smacallan 		 */
909f4a919f6Smacallan 		return;
910f4a919f6Smacallan 	}
911f4a919f6Smacallan 
912f4a919f6Smacallan 	if ((intreg & TS102_CARD_INT_STATUS_IRQ) != 0 &&
913f4a919f6Smacallan 	    (intreg & TS102_CARD_INT_MASK_IRQ) != 0) {
914f4a919f6Smacallan 		/* ignore interrupts if we have a pending state change */
915f4a919f6Smacallan 		if (sc->sc_events & (1 << td->td_slot))
916f4a919f6Smacallan 		{
917f4a919f6Smacallan 			TSPRINTF("ev: %d\n", sc->sc_events);
918f4a919f6Smacallan 			return;
919f4a919f6Smacallan 		}
920f4a919f6Smacallan 		if ((sockstat & TS_CARD) == 0) {
921f4a919f6Smacallan 			printf("%s: spurious interrupt on slot %d isr %x\n",
922266d000aSmacallan 			    device_xname(sc->sc_dev), td->td_slot, intreg);
923f4a919f6Smacallan 			return;
924f4a919f6Smacallan 		}
925f4a919f6Smacallan 
926f4a919f6Smacallan 		if (td->td_intr != NULL) {
927f4a919f6Smacallan 
928f4a919f6Smacallan 			if (td->td_softint != NULL)
9294b293a84Sad 				sparc_softintr_schedule(td->td_softint);
930f4a919f6Smacallan 			/*
931f4a919f6Smacallan 			 * Disable this sbus interrupt, until the soft-int
932f4a919f6Smacallan 			 * handler had a chance to run
933f4a919f6Smacallan 			 */
934f4a919f6Smacallan 			ireg = TSLOT_READ(td, TS102_REG_CARD_A_INT);
935f4a919f6Smacallan 			TSLOT_WRITE(td, TS102_REG_CARD_A_INT, ireg &
936f4a919f6Smacallan 			    ~TS102_CARD_INT_MASK_IRQ);
937f4a919f6Smacallan 		}
938f4a919f6Smacallan 	}
939f4a919f6Smacallan }
940f4a919f6Smacallan 
941648f3c82Smacallan static void
tslot_intr_disestablish(pcmcia_chipset_handle_t pch,void * ih)942f4a919f6Smacallan tslot_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih)
943f4a919f6Smacallan {
944f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
945f4a919f6Smacallan 
946f4a919f6Smacallan 	td->td_intr = NULL;
947f4a919f6Smacallan 	td->td_intrarg = NULL;
948f4a919f6Smacallan 	if (td->td_softint) {
9494b293a84Sad 		sparc_softintr_disestablish(td->td_softint);
950f4a919f6Smacallan 		td->td_softint = NULL;
951f4a919f6Smacallan 	}
952f4a919f6Smacallan }
953f4a919f6Smacallan 
954f4a919f6Smacallan const char *
tslot_intr_string(pcmcia_chipset_handle_t pch,void * ih)955f4a919f6Smacallan tslot_intr_string(pcmcia_chipset_handle_t pch, void *ih)
956f4a919f6Smacallan {
957f4a919f6Smacallan 	if (ih == NULL)
958f4a919f6Smacallan 		return ("couldn't establish interrupt");
959f4a919f6Smacallan 	else
960f4a919f6Smacallan 		return ("");	/* nothing for now */
961f4a919f6Smacallan }
962f4a919f6Smacallan 
963648f3c82Smacallan static void *
tslot_intr_establish(pcmcia_chipset_handle_t pch,struct pcmcia_function * pf,int ipl,int (* handler)(void *),void * arg)964f4a919f6Smacallan tslot_intr_establish(pcmcia_chipset_handle_t pch, struct pcmcia_function *pf,
965648f3c82Smacallan     int ipl, int (*handler)(void *), void *arg)
966f4a919f6Smacallan {
967f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
968f4a919f6Smacallan 
969f4a919f6Smacallan 	td->td_intr = handler;
970f4a919f6Smacallan 	td->td_intrarg = arg;
9714b293a84Sad 	td->td_softint = sparc_softintr_establish(ipl, tslot_intr_dispatch, td);
972f4a919f6Smacallan 
973f4a919f6Smacallan 	return (td);
974f4a919f6Smacallan }
975f4a919f6Smacallan 
976f4a919f6Smacallan /*
977f4a919f6Smacallan  * Softinterrupt called to invoke the real driver interrupt handler.
978f4a919f6Smacallan  */
979f4a919f6Smacallan static void
tslot_intr_dispatch(void * arg)980648f3c82Smacallan tslot_intr_dispatch(void *arg)
981f4a919f6Smacallan {
982f4a919f6Smacallan 	struct tslot_data *td = arg;
983f4a919f6Smacallan 	int s;
984f4a919f6Smacallan 	uint32_t ireg;
985f4a919f6Smacallan 
986f4a919f6Smacallan 	/* invoke driver handler */
987f4a919f6Smacallan 	td->td_intr(td->td_intrarg);
988f4a919f6Smacallan 
989f4a919f6Smacallan 	/* enable SBUS interrupts for pcmcia interrupts again */
990f4a919f6Smacallan 	s = splhigh();
991f4a919f6Smacallan 	ireg = TSLOT_READ(td, TS102_REG_CARD_A_INT);
992f4a919f6Smacallan 	TSLOT_WRITE(td, TS102_REG_CARD_A_INT, ireg | TS102_CARD_INT_MASK_IRQ);
993f4a919f6Smacallan 	splx(s);
994f4a919f6Smacallan }
995f4a919f6Smacallan 
996648f3c82Smacallan static void
tslot_slot_settype(pcmcia_chipset_handle_t pch,int type)997648f3c82Smacallan tslot_slot_settype(pcmcia_chipset_handle_t pch, int type)
998f4a919f6Smacallan {
999f4a919f6Smacallan 	struct tslot_data *td = (struct tslot_data *)pch;
1000f4a919f6Smacallan 	uint32_t reg;
1001f4a919f6Smacallan 
1002f4a919f6Smacallan 	/*
1003f4a919f6Smacallan 	 * Enable the card interrupts if this is an I/O card.
1004f4a919f6Smacallan 	 * Note that the TS102_CARD_STS_IO bit in the status register will
1005f4a919f6Smacallan 	 * never get set, despite what the documentation says!
1006f4a919f6Smacallan 	 */
1007f4a919f6Smacallan 	TSPRINTF("tslot_slot_settype(%d)\n",type);
1008f4a919f6Smacallan 	if (type == PCMCIA_IFTYPE_IO) {
1009f4a919f6Smacallan 		TSLOT_WRITE(td, TS102_REG_CARD_A_STS,
1010f4a919f6Smacallan 		    TSLOT_READ(td, TS102_REG_CARD_A_STS) | TS102_CARD_STS_IO);
1011f4a919f6Smacallan 		TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
1012f4a919f6Smacallan 		    TS102_CARD_INT_MASK_CARDDETECT_STATUS |
1013f4a919f6Smacallan 		    TS102_CARD_INT_MASK_IRQ);
1014f4a919f6Smacallan 		reg=TSLOT_READ(td, TS102_REG_CARD_A_STS);
1015f4a919f6Smacallan 		TSPRINTF("status: %x\n", reg);
1016f4a919f6Smacallan 	}
1017f4a919f6Smacallan }
1018f4a919f6Smacallan 
1019648f3c82Smacallan static void
tslot_update_lcd(struct tslot_softc * sc,int socket,int status)1020f4a919f6Smacallan tslot_update_lcd(struct tslot_softc *sc, int socket, int status)
1021f4a919f6Smacallan {
1022f4a919f6Smacallan #if NTCTRL > 0
1023f4a919f6Smacallan 	int was = (sc->sc_active != 0), is;
1024f4a919f6Smacallan 	int mask = 1 << socket;
1025f4a919f6Smacallan 
1026f4a919f6Smacallan 	if (status > 0) {
1027f4a919f6Smacallan 		sc->sc_active |= mask;
1028f4a919f6Smacallan 	} else {
1029f4a919f6Smacallan 		sc->sc_active &= (mask ^ 3);
1030f4a919f6Smacallan 	}
1031f4a919f6Smacallan 	is = (sc->sc_active != 0);
1032f4a919f6Smacallan 	if (was != is) {
10337d93c08cSmacallan 		tadpole_set_lcd(is, 0x40);
1034f4a919f6Smacallan 	}
1035f4a919f6Smacallan #endif
1036f4a919f6Smacallan }
10376c6277a5Sjdc 
10386c6277a5Sjdc /*
10396c6277a5Sjdc  * Delay and possibly yield CPU.
10406c6277a5Sjdc  * XXX - assumes a context
10416c6277a5Sjdc  */
10426c6277a5Sjdc void
tslot_delay(struct tslot_softc * sc,unsigned int ms)10436c6277a5Sjdc tslot_delay(struct tslot_softc *sc, unsigned int ms)
10446c6277a5Sjdc {
10456c6277a5Sjdc 	unsigned int ticks = mstohz(ms);
10466c6277a5Sjdc 
10476c6277a5Sjdc 	if (cold || ticks == 0) {
10486c6277a5Sjdc 		delay(ms);
10496c6277a5Sjdc 		return;
10506c6277a5Sjdc 	}
10516c6277a5Sjdc 
10526c6277a5Sjdc #ifdef DIAGNOSTIC
10536c6277a5Sjdc 	if (ticks > 60*hz)
10546c6277a5Sjdc 		panic("tslot: preposterous delay: %u", ticks);
10556c6277a5Sjdc #endif
10566c6277a5Sjdc 	tsleep(sc, 0, "tslotdel", ticks);
10576c6277a5Sjdc }
1058