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