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