plumpcmcia.c revision 1.5.4.1 1 /* $NetBSD: plumpcmcia.c,v 1.5.4.1 2001/06/21 19:23:41 nathanw Exp $ */
2
3 /*
4 * Copyright (c) 1999, 2000 UCHIYAMA Yasushi. All rights reserved.
5 * Copyright (c) 1997 Marc Horowitz. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Marc Horowitz.
18 * 4. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "opt_tx39_debug.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/device.h>
38 #include <sys/kthread.h>
39
40 #include <machine/bus.h>
41 #include <machine/config_hook.h>
42
43 #include <dev/pcmcia/pcmciareg.h>
44 #include <dev/pcmcia/pcmciavar.h>
45 #include <dev/pcmcia/pcmciachip.h>
46
47 #include <hpcmips/tx/tx39var.h>
48 #include <hpcmips/dev/plumvar.h>
49 #include <hpcmips/dev/plumicuvar.h>
50 #include <hpcmips/dev/plumpowervar.h>
51 #include <hpcmips/dev/plumpcmciareg.h>
52
53 #ifdef PLUMPCMCIADEBUG
54 #define DPRINTF(arg) printf arg
55 #else
56 #define DPRINTF(arg)
57 #endif
58
59 int plumpcmcia_match(struct device *, struct cfdata *, void *);
60 void plumpcmcia_attach(struct device *, struct device *, void *);
61 int plumpcmcia_print(void *, const char *);
62 int plumpcmcia_submatch(struct device *, struct cfdata *, void *);
63
64 int plumpcmcia_power(void *, int, long, void *);
65
66 struct plumpcmcia_softc;
67
68 struct plumpcmcia_handle {
69 /* parent */
70 struct device *ph_parent;
71 /* child */
72 struct device *ph_pcmcia;
73
74 /* PCMCIA controller register space */
75 bus_space_tag_t ph_regt;
76 bus_space_handle_t ph_regh;
77
78 /* I/O port space */
79 int ph_ioarea; /* not PCMCIA window */
80 struct {
81 bus_addr_t pi_addr;
82 bus_size_t pi_size;
83 int pi_width;
84 } ph_io[PLUM_PCMCIA_IO_WINS];
85 int ph_ioalloc;
86 bus_space_tag_t ph_iot;
87 bus_space_handle_t ph_ioh;
88 bus_addr_t ph_iobase;
89 bus_size_t ph_iosize;
90
91 /* I/O Memory space */
92 int ph_memarea; /* not PCMCIA window */
93 struct {
94 bus_addr_t pm_addr;
95 bus_size_t pm_size;
96 int32_t pm_offset;
97 int pm_kind;
98 } ph_mem[PLUM_PCMCIA_MEM_WINS];
99 int ph_memalloc;
100 bus_space_tag_t ph_memt;
101 bus_space_handle_t ph_memh;
102 bus_addr_t ph_membase;
103 bus_size_t ph_memsize;
104
105 /* Card interrupt handler */
106 int ph_plum_irq;
107 void *ph_card_ih;
108 };
109
110 enum plumpcmcia_event_type {
111 PLUM_PCMCIA_EVENT_INSERT,
112 PLUM_PCMCIA_EVENT_REMOVE,
113 };
114
115 struct plumpcmcia_event {
116 int __queued;
117 enum plumpcmcia_event_type pe_type;
118 struct plumpcmcia_handle *pe_ph;
119 SIMPLEQ_ENTRY(plumpcmcia_event) pe_link;
120 };
121
122 struct plumpcmcia_softc {
123 struct device sc_dev;
124 plum_chipset_tag_t sc_pc;
125
126 /* Register space */
127 bus_space_tag_t sc_regt;
128 bus_space_handle_t sc_regh;
129
130 /* power management hook */
131 void *sc_powerhook;
132
133 /* CSC event */
134 struct proc *sc_event_thread;
135 SIMPLEQ_HEAD (, plumpcmcia_event) sc_event_head;
136
137 /* for each slot */
138 struct plumpcmcia_handle sc_ph[PLUMPCMCIA_NSLOTS];
139 };
140
141 static void plumpcmcia_attach_socket(struct plumpcmcia_handle *);
142 static int plumpcmcia_chip_mem_alloc(pcmcia_chipset_handle_t, bus_size_t,
143 struct pcmcia_mem_handle *);
144 static void plumpcmcia_chip_mem_free(pcmcia_chipset_handle_t,
145 struct pcmcia_mem_handle *);
146 static int plumpcmcia_chip_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t,
147 bus_size_t, struct pcmcia_mem_handle *,
148 bus_addr_t *, int *);
149 static void plumpcmcia_chip_mem_unmap(pcmcia_chipset_handle_t, int);
150 static int plumpcmcia_chip_io_alloc(pcmcia_chipset_handle_t, bus_addr_t,
151 bus_size_t, bus_size_t,
152 struct pcmcia_io_handle *);
153 static void plumpcmcia_chip_io_free(pcmcia_chipset_handle_t,
154 struct pcmcia_io_handle *);
155 static int plumpcmcia_chip_io_map(pcmcia_chipset_handle_t, int, bus_addr_t,
156 bus_size_t, struct pcmcia_io_handle *,
157 int *);
158 static void plumpcmcia_chip_io_unmap(pcmcia_chipset_handle_t, int);
159 static void plumpcmcia_chip_socket_enable(pcmcia_chipset_handle_t);
160 static void plumpcmcia_chip_socket_disable(pcmcia_chipset_handle_t);
161 static void *plumpcmcia_chip_intr_establish(pcmcia_chipset_handle_t,
162 struct pcmcia_function *, int,
163 int (*)(void *), void *);
164 static void plumpcmcia_chip_intr_disestablish(pcmcia_chipset_handle_t, void *);
165 static void plumpcmcia_wait_ready( struct plumpcmcia_handle *);
166 static void plumpcmcia_chip_do_mem_map(struct plumpcmcia_handle *, int);
167 static void plumpcmcia_chip_do_io_map(struct plumpcmcia_handle *, int);
168
169 static struct pcmcia_chip_functions plumpcmcia_functions = {
170 plumpcmcia_chip_mem_alloc,
171 plumpcmcia_chip_mem_free,
172 plumpcmcia_chip_mem_map,
173 plumpcmcia_chip_mem_unmap,
174 plumpcmcia_chip_io_alloc,
175 plumpcmcia_chip_io_free,
176 plumpcmcia_chip_io_map,
177 plumpcmcia_chip_io_unmap,
178 plumpcmcia_chip_intr_establish,
179 plumpcmcia_chip_intr_disestablish,
180 plumpcmcia_chip_socket_enable,
181 plumpcmcia_chip_socket_disable
182 };
183
184 /* CSC */
185 #define PLUM_PCMCIA_EVENT_QUEUE_MAX 5
186 static struct plumpcmcia_event __event_queue_pool[PLUM_PCMCIA_EVENT_QUEUE_MAX];
187 static struct plumpcmcia_event *plumpcmcia_event_alloc(void);
188 static void plumpcmcia_event_free(struct plumpcmcia_event *);
189 static void plum_csc_intr_setup(struct plumpcmcia_softc *,
190 struct plumpcmcia_handle *, int);
191 static int plum_csc_intr(void *);
192 static void plumpcmcia_create_event_thread(void *);
193 static void plumpcmcia_event_thread(void *);
194
195 /* debug */
196 #define __DEBUG_FUNC __attribute__((__unused__))
197 static void __ioareadump(plumreg_t) __DEBUG_FUNC;
198 static void __memareadump(plumreg_t) __DEBUG_FUNC;
199 static void plumpcmcia_dump(struct plumpcmcia_softc *) __DEBUG_FUNC;
200
201 struct cfattach plumpcmcia_ca = {
202 sizeof(struct plumpcmcia_softc), plumpcmcia_match, plumpcmcia_attach
203 };
204
205 int
206 plumpcmcia_match(struct device *parent, struct cfdata *cf, void *aux)
207 {
208 return 1;
209 }
210
211 void
212 plumpcmcia_attach(struct device *parent, struct device *self, void *aux)
213 {
214 struct plum_attach_args *pa = aux;
215 struct plumpcmcia_softc *sc = (void*)self;
216 struct plumpcmcia_handle *ph;
217
218 sc->sc_pc = pa->pa_pc;
219 sc->sc_regt = pa->pa_regt;
220
221 /* map register area */
222 if (bus_space_map(sc->sc_regt, PLUM_PCMCIA_REGBASE,
223 PLUM_PCMCIA_REGSIZE, 0, &sc->sc_regh)) {
224 printf(": register map failed\n");
225 }
226
227 /* power control */
228 plumpcmcia_power(sc, 0, 0, (void *)PWR_RESUME);
229 /* Add a hard power hook to power saving */
230 #if notyet
231 sc->sc_powerhook = config_hook(CONFIG_HOOK_PMEVENT,
232 CONFIG_HOOK_PMEVENT_HARDPOWER,
233 CONFIG_HOOK_SHARE,
234 plumpcmcia_power, sc);
235 if (sc->sc_powerhook == 0)
236 printf(": WARNING unable to establish hard power hook");
237 #endif
238 printf("\n");
239
240 /* Slot0/1 CSC event queue */
241 SIMPLEQ_INIT (&sc->sc_event_head);
242 kthread_create(plumpcmcia_create_event_thread, sc);
243
244 /* Slot 0 */
245 ph = &sc->sc_ph[0];
246 ph->ph_plum_irq = PLUM_INT_C1IO;
247 ph->ph_memarea = PLUM_PCMCIA_MEMWINCTRL_MAP_AREA1;
248 ph->ph_membase = PLUM_PCMCIA_MEMBASE1;
249 ph->ph_memsize = PLUM_PCMCIA_MEMSIZE1;
250 ph->ph_ioarea = PLUM_PCMCIA_IOWINADDRCTRL_AREA1;
251 ph->ph_iobase = PLUM_PCMCIA_IOBASE1;
252 ph->ph_iosize = PLUM_PCMCIA_IOSIZE1;
253 ph->ph_regt = sc->sc_regt;
254 bus_space_subregion(sc->sc_regt, sc->sc_regh,
255 PLUM_PCMCIA_REGSPACE_SLOT0,
256 PLUM_PCMCIA_REGSPACE_SIZE,
257 &ph->ph_regh);
258 ph->ph_iot = pa->pa_iot;
259 ph->ph_memt = pa->pa_iot;
260 ph->ph_parent = (void*)sc;
261
262 plum_csc_intr_setup(sc, ph, PLUM_INT_C1SC);
263 plum_power_establish(sc->sc_pc, PLUM_PWR_PCC1);
264 plumpcmcia_attach_socket(ph);
265
266 /* Slot 1 */
267 ph = &sc->sc_ph[1];
268 ph->ph_plum_irq = PLUM_INT_C2IO;
269 ph->ph_memarea = PLUM_PCMCIA_MEMWINCTRL_MAP_AREA2;
270 ph->ph_membase = PLUM_PCMCIA_MEMBASE2;
271 ph->ph_memsize = PLUM_PCMCIA_MEMSIZE2;
272 ph->ph_ioarea = PLUM_PCMCIA_IOWINADDRCTRL_AREA2;
273 ph->ph_iobase = PLUM_PCMCIA_IOBASE2;
274 ph->ph_iosize = PLUM_PCMCIA_IOSIZE2;
275 ph->ph_regt = sc->sc_regt;
276 bus_space_subregion(sc->sc_regt, sc->sc_regh,
277 PLUM_PCMCIA_REGSPACE_SLOT1,
278 PLUM_PCMCIA_REGSPACE_SIZE,
279 &ph->ph_regh);
280 ph->ph_iot = pa->pa_iot;
281 ph->ph_memt = pa->pa_iot;
282 ph->ph_parent = (void*)sc;
283
284 plum_csc_intr_setup(sc, ph, PLUM_INT_C2SC);
285 plum_power_establish(sc->sc_pc, PLUM_PWR_PCC2);
286 plumpcmcia_attach_socket(ph);
287 }
288
289 int
290 plumpcmcia_print(void *arg, const char *pnp)
291 {
292 if (pnp) {
293 printf("pcmcia at %s", pnp);
294 }
295
296 return UNCONF;
297 }
298
299 int
300 plumpcmcia_submatch(struct device *parent, struct cfdata *cf, void *aux)
301 {
302 return ((*cf->cf_attach->ca_match)(parent, cf, aux));
303 }
304
305 static void
306 plumpcmcia_attach_socket(struct plumpcmcia_handle *ph)
307 {
308 struct pcmciabus_attach_args paa;
309 struct plumpcmcia_softc *sc = (void*)ph->ph_parent;
310
311 paa.paa_busname = "pcmcia";
312 paa.pct = (pcmcia_chipset_tag_t)&plumpcmcia_functions;
313 paa.pch = (pcmcia_chipset_handle_t)ph;
314 paa.iobase = 0;
315 paa.iosize = ph->ph_iosize;
316
317 if ((ph->ph_pcmcia = config_found_sm((void*)sc, &paa,
318 plumpcmcia_print,
319 plumpcmcia_submatch))) {
320 /* Enable slot */
321 plum_conf_write(ph->ph_regt, ph->ph_regh,
322 PLUM_PCMCIA_SLOTCTRL,
323 PLUM_PCMCIA_SLOTCTRL_ENABLE);
324 /* Support 3.3V card & enable Voltage Sense Status */
325 plum_conf_write(ph->ph_regt, ph->ph_regh,
326 PLUM_PCMCIA_FUNCCTRL,
327 PLUM_PCMCIA_FUNCCTRL_VSSEN |
328 PLUM_PCMCIA_FUNCCTRL_3VSUPPORT);
329 pcmcia_card_attach(ph->ph_pcmcia);
330 }
331 }
332
333 static void *
334 plumpcmcia_chip_intr_establish(pcmcia_chipset_handle_t pch,
335 struct pcmcia_function *pf, int ipl,
336 int (*ih_fun)(void *), void *ih_arg)
337 {
338 struct plumpcmcia_handle *ph = (void*)pch;
339 struct plumpcmcia_softc *sc = (void*)ph->ph_parent;
340
341 if (!(ph->ph_card_ih =
342 plum_intr_establish(sc->sc_pc, ph->ph_plum_irq,
343 IST_EDGE, IPL_BIO, ih_fun, ih_arg))) {
344 printf("plumpcmcia_chip_intr_establish: can't establish\n");
345 return 0;
346 }
347
348 return ph->ph_card_ih;
349 }
350
351 static void
352 plumpcmcia_chip_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih)
353 {
354 struct plumpcmcia_handle *ph = (void*)pch;
355 struct plumpcmcia_softc *sc = (void*)ph->ph_parent;
356
357 plum_intr_disestablish(sc->sc_pc, ih);
358 }
359
360 static int
361 plumpcmcia_chip_mem_alloc(pcmcia_chipset_handle_t pch, bus_size_t size,
362 struct pcmcia_mem_handle *pcmhp)
363 {
364 struct plumpcmcia_handle *ph = (void*)pch;
365 bus_size_t realsize;
366
367 /* convert size to PCIC pages */
368 realsize = ((size + (PLUM_PCMCIA_MEM_PAGESIZE - 1)) /
369 PLUM_PCMCIA_MEM_PAGESIZE) * PLUM_PCMCIA_MEM_PAGESIZE;
370
371 if (bus_space_alloc(ph->ph_memt, ph->ph_membase,
372 ph->ph_membase + ph->ph_memsize,
373 realsize, PLUM_PCMCIA_MEM_PAGESIZE,
374 0, 0, 0, &pcmhp->memh)) {
375 return 1;
376 }
377
378 pcmhp->memt = ph->ph_memt;
379 /* Address offset from MEM area base */
380 pcmhp->addr = pcmhp->memh - ph->ph_membase - ph->ph_memt->t_base;
381 pcmhp->size = size;
382 pcmhp->realsize = realsize;
383
384 DPRINTF(("plumpcmcia_chip_mem_alloc: size %#x->%#x addr %#x->%#x\n",
385 (unsigned)size, (unsigned)realsize, (unsigned)pcmhp->addr,
386 (unsigned)pcmhp->memh));
387
388 return 0;
389 }
390
391 static void
392 plumpcmcia_chip_mem_free(pcmcia_chipset_handle_t pch,
393 struct pcmcia_mem_handle *pcmhp)
394 {
395 bus_space_free(pcmhp->memt, pcmhp->memh, pcmhp->size);
396 }
397
398 static int
399 plumpcmcia_chip_mem_map(pcmcia_chipset_handle_t pch, int kind,
400 bus_addr_t card_addr, bus_size_t size,
401 struct pcmcia_mem_handle *pcmhp,
402 bus_addr_t *offsetp, int *windowp)
403 {
404 struct plumpcmcia_handle *ph = (void*)pch;
405 bus_addr_t busaddr;
406 int32_t card_offset;
407 int i, win;
408
409 for (win = -1, i = 0; i < PLUM_PCMCIA_MEM_WINS; i++) {
410 if ((ph->ph_memalloc & (1 << i)) == 0) {
411 win = i;
412 ph->ph_memalloc |= (1 << i);
413 break;
414 }
415 }
416 if (win == -1) {
417 DPRINTF(("plumpcmcia_chip_mem_map: no window\n"));
418 return 1;
419 }
420
421 busaddr = pcmhp->addr;
422
423 *offsetp = card_addr % PLUM_PCMCIA_MEM_PAGESIZE;
424 card_addr -= *offsetp;
425 size += *offsetp - 1;
426 *windowp = win;
427 card_offset = (((int32_t)card_addr) - ((int32_t)busaddr));
428
429 DPRINTF(("plumpcmcia_chip_mem_map window %d bus %#x(kv:%#x)+%#x"
430 " size %#x at card addr %#x offset %#x\n", win,
431 (unsigned)busaddr, (unsigned)pcmhp->memh, (unsigned)*offsetp,
432 (unsigned)size, (unsigned)card_addr, (unsigned)card_offset));
433
434 ph->ph_mem[win].pm_addr = busaddr;
435 ph->ph_mem[win].pm_size = size;
436 ph->ph_mem[win].pm_offset = card_offset;
437 ph->ph_mem[win].pm_kind = kind;
438 ph->ph_memalloc |= (1 << win);
439
440 plumpcmcia_chip_do_mem_map(ph, win);
441
442 return 0;
443 }
444
445 static void
446 plumpcmcia_chip_do_mem_map(struct plumpcmcia_handle *ph, int win)
447 {
448 bus_space_tag_t regt = ph->ph_regt;
449 bus_space_handle_t regh = ph->ph_regh;
450 plumreg_t reg, addr, offset, size;
451
452 if (win < 0 || win > 4) {
453 panic("plumpcmcia_chip_do_mem_map: bogus window %d", win);
454 }
455
456 addr = (ph->ph_mem[win].pm_addr) >> PLUM_PCMCIA_MEM_SHIFT;
457 size = (ph->ph_mem[win].pm_size) >> PLUM_PCMCIA_MEM_SHIFT;
458 offset = (ph->ph_mem[win].pm_offset) >> PLUM_PCMCIA_MEM_SHIFT;
459
460 /* Attribute memory or not */
461 reg = ph->ph_mem[win].pm_kind == PCMCIA_MEM_ATTR ?
462 PLUM_PCMCIA_MEMWINCTRL_REGACTIVE : 0;
463
464 /* Notify I/O area to select for PCMCIA controller */
465 reg = PLUM_PCMCIA_MEMWINCTRL_MAP_SET(reg, ph->ph_memarea);
466
467 /* Zero wait & 16bit access */
468 reg |= (PLUM_PCMCIA_MEMWINCTRL_ZERO_WS |
469 PLUM_PCMCIA_MEMWINCTRL_DATASIZE16);
470 plum_conf_write(regt, regh, PLUM_PCMCIA_MEMWINCTRL(win), reg);
471
472 /* Map Host <-> PC-Card address */
473
474 /* host-side */
475 plum_conf_write(regt, regh, PLUM_PCMCIA_MEMWINSTARTADDR(win),
476 addr);
477 plum_conf_write(regt, regh, PLUM_PCMCIA_MEMWINSTOPADDR(win),
478 addr + size);
479
480 /* card-side */
481 plum_conf_write(regt, regh, PLUM_PCMCIA_MEMWINOFSADDR(win), offset);
482
483 /* Enable memory window */
484 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_WINEN);
485 reg |= PLUM_PCMCIA_WINEN_MEM(win);
486 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, reg);
487
488 DPRINTF(("plumpcmcia_chip_do_mem_map: window:%d %#x(%#x)+%#x\n",
489 win, offset, addr, size));
490
491 delay(100);
492 }
493
494 static void
495 plumpcmcia_chip_mem_unmap(pcmcia_chipset_handle_t pch, int window)
496 {
497 struct plumpcmcia_handle *ph = (void*)pch;
498 bus_space_tag_t regt = ph->ph_regt;
499 bus_space_handle_t regh = ph->ph_regh;
500 plumreg_t reg;
501
502 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_WINEN);
503 reg &= ~PLUM_PCMCIA_WINEN_MEM(window);
504 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, reg);
505
506 ph->ph_memalloc &= ~(1 << window);
507 }
508
509 static int
510 plumpcmcia_chip_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start,
511 bus_size_t size, bus_size_t align,
512 struct pcmcia_io_handle *pcihp)
513 {
514 struct plumpcmcia_handle *ph = (void*)pch;
515
516 DPRINTF(("plumpcmcia_chip_io_alloc: start=%#x size=%#x ",
517 (unsigned)start, (unsigned)size));
518 if (start) {
519 if (bus_space_map(ph->ph_iot, ph->ph_iobase + start,
520 size, 0, &pcihp->ioh)) {
521 DPRINTF(("bus_space_map failed\n"));
522 return 1;
523 }
524 pcihp->flags = 0;
525 pcihp->addr = start;
526 DPRINTF(("(mapped) %#x+%#x\n", (unsigned)start,
527 (unsigned)size));
528 } else {
529 if (bus_space_alloc(ph->ph_iot, ph->ph_iobase,
530 ph->ph_iobase + ph->ph_iosize, size,
531 align, 0, 0, 0, &pcihp->ioh)) {
532 DPRINTF(("bus_space_alloc failed\n"));
533 return 1;
534 }
535 /* Address offset from IO area base */
536 pcihp->addr = pcihp->ioh - ph->ph_iobase -
537 ph->ph_iot->t_base;
538 pcihp->flags = PCMCIA_IO_ALLOCATED;
539 DPRINTF(("(allocated) %#x+%#x\n", (unsigned)pcihp->addr,
540 (unsigned)size));
541 }
542
543 pcihp->iot = ph->ph_iot;
544 pcihp->size = size;
545
546 return 0;
547 }
548
549 static int
550 plumpcmcia_chip_io_map(pcmcia_chipset_handle_t pch, int width,
551 bus_addr_t offset, bus_size_t size,
552 struct pcmcia_io_handle *pcihp, int *windowp)
553 {
554 #ifdef PLUMPCMCIADEBUG
555 static char *width_names[] = { "auto", "io8", "io16" };
556 #endif /* PLUMPCMCIADEBUG */
557 struct plumpcmcia_handle *ph = (void*)pch;
558 bus_addr_t winofs;
559 int i, win;
560
561 winofs = pcihp->addr + offset;
562
563 if (winofs > 0x3ff) {
564 printf("plumpcmcia_chip_io_map: WARNING port %#lx > 0x3ff\n",
565 winofs);
566 }
567
568 for (win = -1, i = 0; i < PLUM_PCMCIA_IO_WINS; i++) {
569 if ((ph->ph_ioalloc & (1 << i)) == 0) {
570 win = i;
571 ph->ph_ioalloc |= (1 << i);
572 break;
573 }
574 }
575 if (win == -1) {
576 DPRINTF(("plumpcmcia_chip_io_map: no window\n"));
577 return 1;
578 }
579 *windowp = win;
580
581 ph->ph_io[win].pi_addr = winofs;
582 ph->ph_io[win].pi_size = size;
583 ph->ph_io[win].pi_width = width;
584
585 plumpcmcia_chip_do_io_map(ph, win);
586
587 DPRINTF(("plumpcmcia_chip_io_map: %#x(kv:%#x)+%#x %s\n",
588 (unsigned)offset, (unsigned)pcihp->ioh, (unsigned)size,
589 width_names[width]));
590
591 return 0;
592 }
593
594 static void
595 plumpcmcia_chip_do_io_map(struct plumpcmcia_handle *ph, int win)
596 {
597 bus_space_tag_t regt = ph->ph_regt;
598 bus_space_handle_t regh = ph->ph_regh;
599 plumreg_t reg;
600 bus_addr_t addr;
601 bus_size_t size;
602 int shift;
603 plumreg_t ioctlbits[3] = {
604 PLUM_PCMCIA_IOWINCTRL_IOCS16SRC,
605 0,
606 PLUM_PCMCIA_IOWINCTRL_DATASIZE16
607 };
608
609 if (win < 0 || win > 1) {
610 panic("plumpcmcia_chip_do_io_map: bogus window %d", win);
611 }
612
613 addr = ph->ph_io[win].pi_addr;
614 size = ph->ph_io[win].pi_size;
615
616 /* Notify I/O area to select for PCMCIA controller */
617 plum_conf_write(regt, regh, PLUM_PCMCIA_IOWINADDRCTRL(win),
618 ph->ph_ioarea);
619
620 /* Start/Stop addr */
621 plum_conf_write(regt, regh, PLUM_PCMCIA_IOWINSTARTADDR(win), addr);
622 plum_conf_write(regt, regh, PLUM_PCMCIA_IOWINSTOPADDR(win),
623 addr + size - 1);
624
625 /* Set bus width */
626 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_IOWINCTRL);
627 shift = win == 0 ? PLUM_PCMCIA_IOWINCTRL_WIN0SHIFT :
628 PLUM_PCMCIA_IOWINCTRL_WIN1SHIFT;
629
630 reg &= ~(PLUM_PCMCIA_IOWINCTRL_WINMASK << shift);
631 reg |= ((ioctlbits[ph->ph_io[win].pi_width] |
632 PLUM_PCMCIA_IOWINCTRL_ZEROWAIT) << shift);
633 plum_conf_write(regt, regh, PLUM_PCMCIA_IOWINCTRL, reg);
634
635 /* Enable window */
636 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_WINEN);
637 reg |= (win == 0 ? PLUM_PCMCIA_WINEN_IO0 :
638 PLUM_PCMCIA_WINEN_IO1);
639 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, reg);
640
641 delay(100);
642 }
643
644 static void
645 plumpcmcia_chip_io_free(pcmcia_chipset_handle_t pch,
646 struct pcmcia_io_handle *pcihp)
647 {
648 if (pcihp->flags & PCMCIA_IO_ALLOCATED) {
649 bus_space_free(pcihp->iot, pcihp->ioh, pcihp->size);
650 } else {
651 bus_space_unmap(pcihp->iot, pcihp->ioh, pcihp->size);
652 }
653
654 DPRINTF(("plumpcmcia_chip_io_free %#x+%#x\n", pcihp->ioh,
655 (unsigned)pcihp->size));
656 }
657
658 static void
659 plumpcmcia_chip_io_unmap(pcmcia_chipset_handle_t pch, int window)
660 {
661 struct plumpcmcia_handle *ph = (void*)pch;
662 bus_space_tag_t regt = ph->ph_regt;
663 bus_space_handle_t regh = ph->ph_regh;
664 plumreg_t reg;
665
666 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_WINEN);
667 switch (window) {
668 default:
669 panic("plumpcmcia_chip_io_unmap: bogus window");
670 case 0:
671 reg &= ~PLUM_PCMCIA_WINEN_IO0;
672 break;
673 case 1:
674 reg &= ~PLUM_PCMCIA_WINEN_IO1;
675 break;
676 }
677 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, reg);
678 ph->ph_ioalloc &= ~(1 << window);
679 }
680
681 static void
682 plumpcmcia_wait_ready(struct plumpcmcia_handle *ph)
683 {
684 bus_space_tag_t regt = ph->ph_regt;
685 bus_space_handle_t regh = ph->ph_regh;
686 int i;
687
688 for (i = 0; i < 10000; i++) {
689 if ((plum_conf_read(regt, regh, PLUM_PCMCIA_STATUS) &
690 PLUM_PCMCIA_STATUS_READY) &&
691 (plum_conf_read(regt, regh, PLUM_PCMCIA_STATUS) &
692 PLUM_PCMCIA_STATUS_PWROK)) {
693 return;
694 }
695 delay(500);
696
697 if ((i > 5000) && (i % 100 == 99)) {
698 printf(".");
699 }
700 }
701 printf("plumpcmcia_wait_ready: failed\n");
702 }
703
704 static void
705 plumpcmcia_chip_socket_enable(pcmcia_chipset_handle_t pch)
706 {
707 struct plumpcmcia_handle *ph = (void*)pch;
708 bus_space_tag_t regt = ph->ph_regt;
709 bus_space_handle_t regh = ph->ph_regh;
710 plumreg_t reg, power;
711 int win, cardtype;
712
713 /* this bit is mostly stolen from pcic_attach_card */
714
715 /* power down the socket to reset it, clear the card reset pin */
716
717 plum_conf_write(regt, regh, PLUM_PCMCIA_PWRCTRL, 0);
718
719 /*
720 * wait 300ms until power fails (Tpf). Then, wait 100ms since
721 * we are changing Vcc (Toff).
722 */
723 delay((300 + 100) * 1000);
724
725 /*
726 * power up the socket
727 */
728 /* detect voltage */
729 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_GENCTRL2);
730 if ((reg & PLUM_PCMCIA_GENCTRL2_VCC5V) ==
731 PLUM_PCMCIA_GENCTRL2_VCC5V) {
732 power = PLUM_PCMCIA_PWRCTRL_VCC_CTRLBIT1; /* 5V */
733 } else {
734 power = PLUM_PCMCIA_PWRCTRL_VCC_CTRLBIT0; /* 3.3V */
735 }
736
737 plum_conf_write(regt, regh, PLUM_PCMCIA_PWRCTRL,
738 PLUM_PCMCIA_PWRCTRL_DISABLE_RESETDRV |
739 power |
740 PLUM_PCMCIA_PWRCTRL_PWR_ENABLE);
741
742 /*
743 * wait 100ms until power raise (Tpr) and 20ms to become
744 * stable (Tsu(Vcc)).
745 *
746 * some machines require some more time to be settled
747 * (300ms is added here).
748 */
749 delay((100 + 20 + 300) * 1000);
750
751 plum_conf_write(regt, regh, PLUM_PCMCIA_PWRCTRL,
752 PLUM_PCMCIA_PWRCTRL_DISABLE_RESETDRV |
753 power |
754 PLUM_PCMCIA_PWRCTRL_OE |
755 PLUM_PCMCIA_PWRCTRL_PWR_ENABLE);
756 plum_conf_write(regt, regh, PLUM_PCMCIA_GENCTRL, 0);
757
758 /*
759 * hold RESET at least 10us.
760 */
761 delay(10);
762
763 /* clear the reset flag */
764 plum_conf_write(regt, regh, PLUM_PCMCIA_GENCTRL,
765 PLUM_PCMCIA_GENCTRL_RESET);
766
767 /* wait 20ms as per pc card standard (r2.01) section 4.3.6 */
768
769 delay(20000);
770
771 /* wait for the chip to finish initializing */
772 plumpcmcia_wait_ready(ph);
773
774 /* zero out the address windows */
775
776 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, 0);
777
778 /* set the card type */
779
780 cardtype = pcmcia_card_gettype(ph->ph_pcmcia);
781
782 reg = (cardtype == PCMCIA_IFTYPE_IO) ?
783 PLUM_PCMCIA_GENCTRL_CARDTYPE_IO :
784 PLUM_PCMCIA_GENCTRL_CARDTYPE_MEM;
785 reg |= plum_conf_read(regt, regh, PLUM_PCMCIA_GENCTRL);
786 DPRINTF(("%s: plumpcmcia_chip_socket_enable cardtype %s\n",
787 ph->ph_parent->dv_xname,
788 ((cardtype == PCMCIA_IFTYPE_IO) ? "io" : "mem")));
789
790 plum_conf_write(regt, regh, PLUM_PCMCIA_GENCTRL, reg);
791
792 /* reinstall all the memory and io mappings */
793
794 for (win = 0; win < PLUM_PCMCIA_MEM_WINS; win++) {
795 if (ph->ph_memalloc & (1 << win)) {
796 plumpcmcia_chip_do_mem_map(ph, win);
797 }
798 }
799
800 for (win = 0; win < PLUM_PCMCIA_IO_WINS; win++) {
801 if (ph->ph_ioalloc & (1 << win)) {
802 plumpcmcia_chip_do_io_map(ph, win);
803 }
804 }
805
806 }
807
808 static void
809 plumpcmcia_chip_socket_disable(pcmcia_chipset_handle_t pch)
810 {
811 struct plumpcmcia_handle *ph = (void*)pch;
812 bus_space_tag_t regt = ph->ph_regt;
813 bus_space_handle_t regh = ph->ph_regh;
814
815 /* power down the socket */
816 plum_conf_write(regt, regh, PLUM_PCMCIA_PWRCTRL, 0);
817
818 /*
819 * wait 300ms until power fails (Tpf).
820 */
821 delay(300 * 1000);
822 }
823
824 static void
825 plum_csc_intr_setup(struct plumpcmcia_softc *sc, struct plumpcmcia_handle *ph,
826 int irq)
827 {
828 bus_space_tag_t regt = ph->ph_regt;
829 bus_space_handle_t regh = ph->ph_regh;
830 plumreg_t reg;
831 void *ih;
832
833 /* enable CARD DETECT ENABLE only */
834 plum_conf_write(regt, regh, PLUM_PCMCIA_CSCINT,
835 PLUM_PCMCIA_CSCINT_CARD_DETECT);
836
837 /* don't use explicit writeback csc interrupt status */
838 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_GLOBALCTRL);
839 reg &= ~PLUM_PCMCIA_GLOBALCTRL_EXPLICIT_WB_CSC_INT;
840 plum_conf_write(regt, regh, PLUM_PCMCIA_GLOBALCTRL, reg);
841
842 /* install interrupt handler (don't fail) */
843 ih = plum_intr_establish(sc->sc_pc, irq, IST_EDGE, IPL_TTY,
844 plum_csc_intr, ph);
845 KASSERT(ih != 0);
846 }
847
848 static int
849 plum_csc_intr(void *arg)
850 {
851 struct plumpcmcia_handle *ph = arg;
852 struct plumpcmcia_softc *sc = (void *)ph->ph_parent;
853 struct plumpcmcia_event *pe;
854 bus_space_tag_t regt = ph->ph_regt;
855 bus_space_handle_t regh = ph->ph_regh;
856 plumreg_t reg;
857 int flag;
858
859 /* read and clear interrupt status */
860 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_CSCINT_STAT);
861 if (reg & PLUM_PCMCIA_CSCINT_CARD_DETECT) {
862 DPRINTF(("%s: card status change.\n", __FUNCTION__));
863 } else {
864 DPRINTF(("%s: unhandled csc event. 0x%02x\n",
865 __FUNCTION__, reg));
866 return 0;
867 }
868
869 /* inquire card status (insert or remove) */
870 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_STATUS);
871 reg &= (PLUM_PCMCIA_STATUS_CD1 | PLUM_PCMCIA_STATUS_CD2);
872 if (reg == (PLUM_PCMCIA_STATUS_CD1 | PLUM_PCMCIA_STATUS_CD2)) {
873 /* insert */
874 flag = PLUM_PCMCIA_EVENT_INSERT;
875 } else {
876 /* remove */
877 flag = PLUM_PCMCIA_EVENT_REMOVE;
878 }
879
880 /* queue event to event thread and wakeup. */
881 pe = plumpcmcia_event_alloc();
882 if (pe == 0) {
883 printf("%s: event FIFO overflow (%d).\n", __FUNCTION__,
884 PLUM_PCMCIA_EVENT_QUEUE_MAX);
885 return 0;
886 }
887 pe->pe_type = flag;
888 pe->pe_ph = ph;
889 SIMPLEQ_INSERT_TAIL(&sc->sc_event_head, pe, pe_link);
890 wakeup(sc);
891
892 return 0;
893 }
894
895 static void
896 __memareadump(plumreg_t reg)
897 {
898 int maparea;
899
900 maparea = PLUM_PCMCIA_MEMWINCTRL_MAP(reg);
901 switch (maparea) {
902 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA1:
903 printf("MEM Area1\n");
904 break;
905 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA2:
906 printf("MEM Area2\n");
907 break;
908 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA3:
909 printf("MEM Area3\n");
910 break;
911 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA4:
912 printf("MEM Area4\n");
913 break;
914 }
915 }
916
917 static struct plumpcmcia_event *
918 plumpcmcia_event_alloc()
919 {
920 int i;
921 /* I assume called from interrupt context only. so don't lock */
922 for (i = 0; i < PLUM_PCMCIA_EVENT_QUEUE_MAX; i++) {
923 if (!__event_queue_pool[i].__queued) {
924 __event_queue_pool[i].__queued = 1;
925 return &__event_queue_pool[i];
926 }
927 }
928
929 return 0;
930 }
931
932 static void
933 plumpcmcia_event_free(struct plumpcmcia_event *pe)
934 {
935 /* I assume context is already locked */
936 pe->__queued = 0;
937 }
938
939 static void
940 plumpcmcia_create_event_thread(void *arg)
941 {
942 struct plumpcmcia_softc *sc = arg;
943 int error;
944
945 error = kthread_create1(plumpcmcia_event_thread, sc,
946 &sc->sc_event_thread, "%s",
947 sc->sc_dev.dv_xname);
948 KASSERT(error == 0);
949 }
950
951 static void
952 plumpcmcia_event_thread(void *arg)
953 {
954 struct plumpcmcia_softc *sc = arg;
955 struct plumpcmcia_event *pe;
956 int s;
957
958 while (/*CONSTCOND*/1) { /* XXX shutdown. -uch */
959 tsleep(sc, PWAIT, "CSC wait", 0);
960 s = spltty();
961 while ((pe = SIMPLEQ_FIRST(&sc->sc_event_head))) {
962 splx(s);
963 switch (pe->pe_type) {
964 default:
965 printf("%s: unknown event.\n", __FUNCTION__);
966 break;
967 case PLUM_PCMCIA_EVENT_INSERT:
968 DPRINTF(("%s: insert event.\n", __FUNCTION__));
969 pcmcia_card_attach(pe->pe_ph->ph_pcmcia);
970 break;
971 case PLUM_PCMCIA_EVENT_REMOVE:
972 DPRINTF(("%s: remove event.\n", __FUNCTION__));
973 pcmcia_card_detach(pe->pe_ph->ph_pcmcia,
974 DETACH_FORCE);
975 break;
976 }
977 s = spltty();
978 SIMPLEQ_REMOVE_HEAD(&sc->sc_event_head, pe, pe_link);
979 plumpcmcia_event_free(pe);
980 }
981 splx(s);
982 }
983 /* NOTREACHED */
984 }
985
986 /* power XXX notyet */
987 int
988 plumpcmcia_power(void *ctx, int type, long id, void *msg)
989 {
990 struct plumpcmcia_softc *sc = ctx;
991 bus_space_tag_t regt = sc->sc_regt;
992 bus_space_handle_t regh = sc->sc_regh;
993 int why = (int)msg;
994
995 switch (why) {
996 case PWR_RESUME:
997 DPRINTF(("%s: ON\n", sc->sc_dev.dv_xname));
998 /* power on */
999 plum_conf_write(regt, regh, PLUM_PCMCIA_CARDPWRCTRL,
1000 PLUM_PCMCIA_CARDPWRCTRL_ON);
1001 break;
1002 case PWR_SUSPEND:
1003 /* FALLTHROUGH */
1004 case PWR_STANDBY:
1005 plum_conf_write(regt, regh, PLUM_PCMCIA_CARDPWRCTRL,
1006 PLUM_PCMCIA_CARDPWRCTRL_OFF);
1007 DPRINTF(("%s: OFF\n", sc->sc_dev.dv_xname));
1008 break;
1009 }
1010
1011 return 0;
1012 }
1013
1014 static void
1015 __ioareadump(plumreg_t reg)
1016 {
1017 if (reg & PLUM_PCMCIA_IOWINADDRCTRL_AREA2) {
1018 printf("I/O Area 2\n");
1019 } else {
1020 printf("I/O Area 1\n");
1021 }
1022 }
1023
1024 static void
1025 plumpcmcia_dump(struct plumpcmcia_softc *sc)
1026 {
1027 bus_space_tag_t regt = sc->sc_regt;
1028 bus_space_handle_t regh = sc->sc_regh;
1029 plumreg_t reg;
1030
1031 int i, j;
1032
1033 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN0CTRL));
1034 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN1CTRL));
1035 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN2CTRL));
1036 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN3CTRL));
1037 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN4CTRL));
1038
1039 __ioareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_IOWIN0ADDRCTRL));
1040 __ioareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_IOWIN1ADDRCTRL));
1041
1042 for (j = 0; j < 2; j++) {
1043 printf("[slot %d]\n", j);
1044 for (i = 0; i < 0x120; i += 4) {
1045 reg = plum_conf_read(sc->sc_regt, sc->sc_regh, i + 0x800 * j);
1046 printf("%03x %08x", i, reg);
1047 bitdisp(reg);
1048 }
1049 }
1050 printf("\n");
1051 }
1052