1 /* $OpenBSD: i82365_cbus.c,v 1.9 2024/06/01 00:48:16 aoyama Exp $ */
2 /* $NetBSD: i82365_isa.c,v 1.11 1998/06/09 07:25:00 thorpej Exp $ */
3
4 /*
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 /*
34 * Driver for PC-9801-102 & PC-9821X[AE]-E01 PC Card slot adapter
35 * based on OpenBSD:src/sys/dev/isa/i82365_isa{,subr}.c
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/device.h>
42 #include <sys/extent.h>
43 #include <sys/malloc.h>
44
45 #include <machine/board.h> /* PC_BASE */
46 #include <machine/bus.h>
47 #include <machine/intr.h>
48
49 #include <arch/luna88k/cbus/cbusvar.h>
50
51 #include <dev/pcmcia/pcmciareg.h>
52 #include <dev/pcmcia/pcmciavar.h>
53 #include <dev/pcmcia/pcmciachip.h>
54
55 #include <dev/ic/i82365reg.h>
56 #include <dev/ic/i82365var.h>
57
58 #ifdef PCICCBUSDEBUG
59 #define DPRINTF(arg) printf arg;
60 #else
61 #define DPRINTF(arg)
62 #endif
63
64 /*
65 * XXX:
66 * The C-bus expects edge-triggered interrupts, but some PC Cards and
67 * the controller itself produce level-triggered interrupts. This causes
68 * spurious interrupts on C-bus.
69 * Then, we use CL-PD67XX 'pulse IRQ' feature in this driver. This seems
70 * to solve stray interrupts on C-bus.
71 * (BTW, all NEC genuine C-bus PC Card slot adapters use CL-PD67XX)
72 */
73
74 /* Cirrus Logic CL-PD67XX Misc Control register */
75 #define PCIC_CIRRUS_MISC_CTL_1 0x16
76 #define PCIC_CIRRUS_MISC_CTL_1_PULSE_MGMT_INTR 0x04
77 #define PCIC_CIRRUS_MISC_CTL_1_PULSE_SYS_IRQ 0x08
78
79 /* prototypes */
80 void *pcic_cbus_chip_intr_establish(pcmcia_chipset_handle_t,
81 struct pcmcia_function *, int, int (*) (void *), void *, char *);
82 void pcic_cbus_chip_intr_disestablish(pcmcia_chipset_handle_t, void *);
83 const char *pcic_cbus_chip_intr_string(pcmcia_chipset_handle_t, void *);
84 int pcic_cbus_intlevel_find(void);
85 int pcic_cbus_chip_io_alloc(pcmcia_chipset_handle_t, bus_addr_t,
86 bus_size_t, bus_size_t, struct pcmcia_io_handle *);
87 void pcic_cbus_chip_io_free(pcmcia_chipset_handle_t,
88 struct pcmcia_io_handle *);
89
90 int pcic_cbus_probe(struct device *, void *, void *);
91 void pcic_cbus_attach(struct device *, struct device *, void *);
92
93 /* bus space tag for pcic_cbus */
94 struct luna88k_bus_space_tag pcic_cbus_io_bst = {
95 .bs_stride_1 = 0,
96 .bs_stride_2 = 0,
97 .bs_stride_4 = 0,
98 .bs_stride_8 = 0, /* not used */
99 .bs_offset = PCEXIO_BASE,
100 .bs_flags = TAG_LITTLE_ENDIAN
101 };
102
103 struct luna88k_bus_space_tag pcic_cbus_mem_bst = {
104 .bs_stride_1 = 0,
105 .bs_stride_2 = 0,
106 .bs_stride_4 = 0,
107 .bs_stride_8 = 0, /* not used */
108 .bs_offset = PCEXMEM_BASE,
109 .bs_flags = TAG_LITTLE_ENDIAN
110 };
111
112 const struct cfattach pcic_cbus_ca = {
113 sizeof(struct pcic_softc), pcic_cbus_probe, pcic_cbus_attach
114 };
115
116 static struct pcmcia_chip_functions pcic_cbus_functions = {
117 .mem_alloc = pcic_chip_mem_alloc,
118 .mem_free = pcic_chip_mem_free,
119 .mem_map = pcic_chip_mem_map,
120 .mem_unmap = pcic_chip_mem_unmap,
121
122 .io_alloc = pcic_cbus_chip_io_alloc,
123 .io_free = pcic_cbus_chip_io_free,
124 .io_map = pcic_chip_io_map,
125 .io_unmap = pcic_chip_io_unmap,
126
127 .intr_establish = pcic_cbus_chip_intr_establish,
128 .intr_disestablish = pcic_cbus_chip_intr_disestablish,
129 .intr_string = pcic_cbus_chip_intr_string,
130
131 .socket_enable = pcic_chip_socket_enable,
132 .socket_disable = pcic_chip_socket_disable,
133 };
134
135 /*
136 * NEC PC-9801 architecture uses different IRQ notation from PC-AT
137 * architecture, so-called INT. The MI pcic(4) driver internally uses
138 * IRQ, so here is a table to convert INT to IRQ.
139 */
140 static const int pcic_cbus_int2irq[NCBUSISR] = {
141 PCIC_INTR_IRQ3, /* INT 0 */
142 PCIC_INTR_IRQ5, /* INT 1 */
143 PCIC_INTR_IRQ_RESERVED6, /* INT 2 */
144 PCIC_INTR_IRQ9, /* INT 3 */
145 PCIC_INTR_IRQ10, /* INT 4(41) */
146 PCIC_INTR_IRQ12, /* INT 5 */
147 PCIC_INTR_IRQ_RESERVED13 /* INT 6 */
148 };
149
150 /* And, a table to convert IRQ to INT */
151 static const int pcic_cbus_irq2int[] = {
152 -1, -1, -1, 0, -1, 1, 2, -1, /* IRQ 0- 7 */
153 -1, 3, 4, -1, 5, 6, -1, -1 /* IRQ 8-15 */
154 };
155
156 struct pcic_ranges pcic_cbus_addr[] = {
157 { 0x340, 0x030 },
158 { 0x300, 0x030 },
159 { 0x390, 0x020 },
160 { 0x400, 0xbff },
161 { 0, 0}, /* terminator */
162 };
163
164 int
pcic_cbus_probe(parent,match,aux)165 pcic_cbus_probe(parent, match, aux)
166 struct device *parent;
167 void *match, *aux;
168 {
169 struct cfdata *cf = match;
170 struct cbus_attach_args *caa = aux;
171 bus_space_tag_t iot = &pcic_cbus_io_bst;
172 bus_space_tag_t memt = &pcic_cbus_mem_bst;
173 bus_space_handle_t ioh, memh;
174 bus_size_t msize;
175 int val, found;
176
177 if (strcmp(caa->ca_name, cf->cf_driver->cd_name) != 0)
178 return (0);
179
180 SET_TAG_LITTLE_ENDIAN(iot);
181 SET_TAG_LITTLE_ENDIAN(memt);
182
183 caa->ca_iobase = cf->cf_iobase;
184 caa->ca_maddr = cf->cf_maddr;
185 caa->ca_msize = cf->cf_msize;
186 caa->ca_int = cf->cf_int;
187
188 /* Disallow wildcarded i/o address. */
189 if (caa->ca_iobase == -1)
190 return (0);
191
192 if (bus_space_map(iot, caa->ca_iobase, PCIC_IOSIZE, 0, &ioh))
193 return (0);
194
195 if (caa->ca_msize == -1)
196 msize = PCIC_MEMSIZE;
197 if (bus_space_map(memt, caa->ca_maddr, msize, 0, &memh))
198 return (0);
199
200 found = 0;
201
202 /*
203 * this could be done with a loop, but it would violate the
204 * abstraction
205 */
206
207 bus_space_write_1(iot, ioh, PCIC_REG_INDEX, C0SA + PCIC_IDENT);
208 val = bus_space_read_1(iot, ioh, PCIC_REG_DATA);
209 if (pcic_ident_ok(val))
210 found++;
211
212 bus_space_write_1(iot, ioh, PCIC_REG_INDEX, C0SB + PCIC_IDENT);
213 val = bus_space_read_1(iot, ioh, PCIC_REG_DATA);
214 if (pcic_ident_ok(val))
215 found++;
216
217 bus_space_write_1(iot, ioh, PCIC_REG_INDEX, C1SA + PCIC_IDENT);
218 val = bus_space_read_1(iot, ioh, PCIC_REG_DATA);
219 if (pcic_ident_ok(val))
220 found++;
221
222 bus_space_write_1(iot, ioh, PCIC_REG_INDEX, C1SB + PCIC_IDENT);
223 val = bus_space_read_1(iot, ioh, PCIC_REG_DATA);
224 if (pcic_ident_ok(val))
225 found++;
226
227 bus_space_unmap(iot, ioh, PCIC_IOSIZE);
228 bus_space_unmap(memt, memh, msize);
229
230 if (!found)
231 return (0);
232 caa->ca_iosize = PCIC_IOSIZE;
233 caa->ca_msize = msize;
234 return (1);
235 }
236
237 void
pcic_cbus_attach(parent,self,aux)238 pcic_cbus_attach(parent, self, aux)
239 struct device *parent, *self;
240 void *aux;
241 {
242 struct pcic_softc *sc = (void *)self;
243 struct pcic_handle *h;
244 struct cbus_attach_args *caa = aux;
245 bus_space_tag_t iot = &pcic_cbus_io_bst;
246 bus_space_tag_t memt = &pcic_cbus_mem_bst;
247 bus_space_handle_t ioh;
248 bus_space_handle_t memh;
249 int intlevel, irq, i, reg;
250
251 SET_TAG_LITTLE_ENDIAN(iot);
252 SET_TAG_LITTLE_ENDIAN(memt);
253
254 /* Map i/o space. */
255 if (bus_space_map(iot, caa->ca_iobase, caa->ca_iosize, 0, &ioh)) {
256 printf(": can't map i/o space\n");
257 return;
258 }
259
260 /* Map mem space. */
261 if (bus_space_map(memt, caa->ca_maddr, caa->ca_msize, 0, &memh)) {
262 printf(": can't map mem space\n");
263 return;
264 }
265
266 sc->membase = caa->ca_maddr;
267 sc->subregionmask = (1 << (caa->ca_msize / PCIC_MEM_PAGESIZE)) - 1;
268
269 sc->intr_est = NULL; /* not used on luna88k */
270 sc->pct = (pcmcia_chipset_tag_t)&pcic_cbus_functions;
271
272 sc->iot = iot;
273 sc->ioh = ioh;
274 sc->memt = memt;
275 sc->memh = memh;
276
277 printf("\n");
278
279 pcic_attach(sc);
280
281 sc->ranges = pcic_cbus_addr;
282 sc->iobase = 0x0000;
283 sc->iosize = 0x1000;
284 DPRINTF(("%s: bus_space_alloc range 0x%04lx-0x%04lx\n",
285 sc->dev.dv_xname, (long) sc->iobase,
286 (long) sc->iobase + sc->iosize));
287
288 pcic_attach_sockets(sc);
289
290 /*
291 * Allocate an INT. It will be used by both controllers. We could
292 * use two different interrupts, but interrupts are relatively
293 * scarce, shareable, and for PCIC controllers, very infrequent.
294 */
295 intlevel = pcic_cbus_intlevel_find();
296 if (intlevel == -1) {
297 printf("pcic_cbus_attach: no free int found\n");
298 return;
299 }
300
301 irq = pcic_cbus_int2irq[intlevel];
302 cbus_isrlink(pcic_intr, sc, intlevel, IPL_TTY, sc->dev.dv_xname);
303 sc->ih = (void *)pcic_intr;
304 sc->irq = irq;
305
306 if (irq) {
307 printf("%s: int %d (irq %d), ", sc->dev.dv_xname,
308 intlevel, irq);
309
310 /* Set up the pcic to interrupt on card detect. */
311 for (i = 0; i < PCIC_NSLOTS; i++) {
312 h = &sc->handle[i];
313 if (h->flags & PCIC_FLAG_SOCKETP) {
314 /* set 'pulse management interrupt' mode */
315 reg = pcic_read(h, PCIC_CIRRUS_MISC_CTL_1);
316 reg |= PCIC_CIRRUS_MISC_CTL_1_PULSE_MGMT_INTR;
317 pcic_write(h, PCIC_CIRRUS_MISC_CTL_1, reg);
318
319 pcic_write(h, PCIC_CSC_INTR,
320 (sc->irq << PCIC_CSC_INTR_IRQ_SHIFT) |
321 PCIC_CSC_INTR_CD_ENABLE);
322 }
323 }
324 } else
325 printf("%s: no int, ", sc->dev.dv_xname);
326
327 printf("polling enabled\n");
328 if (sc->poll_established == 0) {
329 timeout_set(&sc->poll_timeout, pcic_poll_intr, sc);
330 timeout_add_msec(&sc->poll_timeout, 500);
331 sc->poll_established = 1;
332 }
333 }
334
335 void *
pcic_cbus_chip_intr_establish(pcmcia_chipset_handle_t pch,struct pcmcia_function * pf,int ipl,int (* fcl)(void *),void * arg,char * xname)336 pcic_cbus_chip_intr_establish(pcmcia_chipset_handle_t pch,
337 struct pcmcia_function *pf, int ipl, int (*fcl)(void *),
338 void *arg, char *xname)
339 {
340 struct pcic_handle *h = (struct pcic_handle *)pch;
341 #ifdef PCICCBUSDEBUG
342 struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent);
343 #endif
344 int intlevel, irq, reg;
345
346 #ifdef PCICCBUSDEBUG
347 char buf[16];
348 if (pf->cfe->flags & PCMCIA_CFE_IRQLEVEL)
349 strlcpy(buf, "LEVEL", sizeof(buf));
350 else if (pf->cfe->flags & PCMCIA_CFE_IRQPULSE)
351 strlcpy(buf, "PULSE", sizeof(buf));
352 else
353 strlcpy(buf, "EDGE", sizeof(buf));
354 printf("pcic_cbus_chip_intr_establish: IST_%s\n", buf);
355 #endif
356
357 /*
358 * If the PC Card has level-triggered interrupt property,
359 * we use CL-PD67XX 'pulse IRQ' feature.
360 */
361 if (pf->cfe->flags & PCMCIA_CFE_IRQLEVEL) {
362 reg = pcic_read(h, PCIC_CIRRUS_MISC_CTL_1);
363 reg |= PCIC_CIRRUS_MISC_CTL_1_PULSE_SYS_IRQ;
364 pcic_write(h, PCIC_CIRRUS_MISC_CTL_1, reg);
365 }
366
367 intlevel = pcic_cbus_intlevel_find();
368
369 if (intlevel == -1) {
370 printf("pcic_cbus_chip_intr_establish: no int found\n");
371 return (NULL);
372 }
373
374 irq = pcic_cbus_int2irq[intlevel];
375 h->ih_irq = irq;
376
377 DPRINTF(("%s: pcic_cbus_chip_intr_establish int %d (irq %d)\n",
378 sc->dev.dv_xname, intlevel, h->ih_irq));
379
380 cbus_isrlink(fcl, arg, intlevel, ipl, h->pcmcia->dv_xname);
381
382 reg = pcic_read(h, PCIC_INTR);
383 reg &= ~(PCIC_INTR_IRQ_MASK | PCIC_INTR_ENABLE);
384 pcic_write(h, PCIC_INTR, reg | irq);
385
386 return (void *)fcl;
387 }
388
389 void
pcic_cbus_chip_intr_disestablish(pcmcia_chipset_handle_t pch,void * ih)390 pcic_cbus_chip_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih)
391 {
392 struct pcic_handle *h = (struct pcic_handle *)pch;
393 #ifdef PCICCBUSDEBUG
394 struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent);
395 #endif
396 int intlevel, reg;
397
398 intlevel = pcic_cbus_irq2int[h->ih_irq];
399
400 DPRINTF(("%s: pcic_cbus_chip_intr_disestablish int %d (irq %d)\n",
401 sc->dev.dv_xname, intlevel, h->ih_irq));
402
403 if (intlevel == -1) {
404 printf("pcic_cbus_chip_intr_disestablish: "
405 "strange int (irq = %d)\n", h->ih_irq);
406 return;
407 }
408
409 h->ih_irq = 0;
410
411 reg = pcic_read(h, PCIC_INTR);
412 reg &= ~(PCIC_INTR_IRQ_MASK | PCIC_INTR_ENABLE);
413 pcic_write(h, PCIC_INTR, reg);
414
415 cbus_isrunlink(ih, intlevel);
416
417 /* reset the 'pulse IRQ' mode */
418 reg = pcic_read(h, PCIC_CIRRUS_MISC_CTL_1);
419 reg &= ~PCIC_CIRRUS_MISC_CTL_1_PULSE_SYS_IRQ;
420 pcic_write(h, PCIC_CIRRUS_MISC_CTL_1, reg);
421 }
422
423 const char *
pcic_cbus_chip_intr_string(pcmcia_chipset_handle_t pch,void * ih)424 pcic_cbus_chip_intr_string(pcmcia_chipset_handle_t pch, void *ih)
425 {
426 struct pcic_handle *h = (struct pcic_handle *)pch;
427 static char irqstr[64];
428
429 if (ih == NULL)
430 snprintf(irqstr, sizeof(irqstr),
431 "couldn't establish interrupt");
432 else
433 snprintf(irqstr, sizeof(irqstr), "int %d (irq %d)",
434 pcic_cbus_irq2int[h->ih_irq], h->ih_irq);
435 return(irqstr);
436 }
437
438 /*
439 * Find a free and pcic-compliant INT level; searching from highest
440 * (=small number) to lowest.
441 */
442 int
pcic_cbus_intlevel_find(void)443 pcic_cbus_intlevel_find(void)
444 {
445 int intlevel, irq;
446 u_int8_t cbus_not_used = ~cbus_intr_registered();
447
448 for (intlevel = 0; intlevel < NCBUSISR; intlevel++)
449 if (cbus_not_used & (1 << (6 - intlevel))) {
450 irq = pcic_cbus_int2irq[intlevel];
451 if ((1 << irq) & PCIC_INTR_IRQ_VALIDMASK)
452 break;
453 }
454
455 if (intlevel == NCBUSISR)
456 intlevel = -1; /* not found */
457
458 return intlevel;
459 }
460
461 /*
462 * LUNA specific pcic_cbus_chip_io_{alloc,free}
463 */
464 int
pcic_cbus_chip_io_alloc(pcmcia_chipset_handle_t pch,bus_addr_t start,bus_size_t size,bus_size_t align,struct pcmcia_io_handle * pcihp)465 pcic_cbus_chip_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start,
466 bus_size_t size, bus_size_t align, struct pcmcia_io_handle *pcihp)
467 {
468 struct pcic_handle *h = (struct pcic_handle *) pch;
469 bus_space_tag_t iot;
470 bus_space_handle_t ioh;
471 bus_addr_t ioaddr, beg, fin;
472 int flags = 0;
473 struct pcic_softc *sc = (struct pcic_softc *)(h->ph_parent);
474 struct pcic_ranges *range;
475
476 /*
477 * Allocate some arbitrary I/O space.
478 */
479
480 iot = sc->iot;
481
482 if (start) {
483 ioaddr = start;
484 if (bus_space_map(iot, start, size, 0, &ioh))
485 return (1);
486 DPRINTF(("pcic_cbus_chip_io_alloc map port %lx+%lx\n",
487 (u_long)ioaddr, (u_long)size));
488 } else if (sc->ranges) {
489 /*
490 * In this case, we know the "size" and "align" that
491 * we want. So we need to start walking down
492 * sc->ranges, searching for a similar space that
493 * is (1) large enough for the size and alignment
494 * (2) then we need to try to allocate
495 * (3) if it fails to allocate, we try next range.
496 *
497 * We must also check that the start/size of each
498 * allocation we are about to do is within the bounds
499 * of "sc->iobase" and "sc->iosize".
500 * (Some pcmcia controllers handle a 12 bits of addressing,
501 * but we want to use the same range structure)
502 */
503 for (range = sc->ranges; range->start; range++) {
504 /* Potentially trim the range because of bounds. */
505 beg = max(range->start, sc->iobase);
506 fin = min(range->start + range->len,
507 sc->iobase + sc->iosize);
508
509 /* Short-circuit easy cases. */
510 if (fin < beg || fin - beg < size)
511 continue;
512
513 DPRINTF(("pcic_cbus_chip_io_alloc beg-fin %lx-%lx\n",
514 (u_long)beg, (u_long)fin));
515 if (bus_space_map(iot, beg, size, 0, &ioh) == 0) {
516 ioaddr = beg;
517 break;
518 }
519 }
520 if (range->start == 0)
521 return (1);
522 DPRINTF(("pcic_cbus_chip_io_alloc alloc port %lx+%lx\n",
523 (u_long)ioaddr, (u_long)size));
524 } else {
525 if (bus_space_map(iot, sc->iobase, size, 0, &ioh))
526 return (1);
527 ioaddr = sc->iobase;
528 DPRINTF(("pcic_cbus_chip_io_alloc alloc port %lx+%lx\n",
529 (u_long)ioaddr, (u_long)size));
530 }
531
532 pcihp->iot = iot;
533 pcihp->ioh = ioh;
534 pcihp->addr = ioaddr;
535 pcihp->size = size;
536 pcihp->flags = flags;
537
538 return (0);
539 }
540
541 void
pcic_cbus_chip_io_free(pcmcia_chipset_handle_t pch,struct pcmcia_io_handle * pcihp)542 pcic_cbus_chip_io_free(pcmcia_chipset_handle_t pch,
543 struct pcmcia_io_handle *pcihp)
544 {
545 bus_space_tag_t iot = pcihp->iot;
546 bus_space_handle_t ioh = pcihp->ioh;
547 bus_size_t size = pcihp->size;
548
549 bus_space_unmap(iot, ioh, size);
550 }
551