xref: /openbsd-src/sys/arch/macppc/pci/mpcpcibus.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: mpcpcibus.c,v 1.46 2013/08/07 07:29:19 mpi Exp $ */
2 
3 /*
4  * Copyright (c) 1997 Per Fogelstrom
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/device.h>
32 #include <sys/malloc.h>
33 #include <sys/extent.h>
34 
35 #include <machine/autoconf.h>
36 #include <machine/pcb.h>
37 
38 #include <dev/pci/pcireg.h>
39 #include <dev/pci/pcivar.h>
40 #include <dev/pci/pcidevs.h>
41 
42 #include <dev/ofw/openfirm.h>
43 
44 int	mpcpcibrmatch(struct device *, void *, void *);
45 void	mpcpcibrattach(struct device *, struct device *, void *);
46 
47 pcireg_t mpc_conf_read(void *, pcitag_t, int);
48 void	mpc_conf_write(void *, pcitag_t, int, pcireg_t);
49 
50 u_int32_t mpc_gen_config_reg(void *cpv, pcitag_t tag, int offset);
51 
52 struct pcibr_config {
53 	bus_space_tag_t		lc_memt;
54 	bus_space_tag_t		lc_iot;
55 	bus_space_handle_t	ioh_cf8;
56 	bus_space_handle_t	ioh_cfc;
57 	struct ppc_pci_chipset	lc_pc;
58 	int			config_type;
59 };
60 
61 struct pcibr_softc {
62 	struct device		sc_dev;
63 	struct ppc_bus_space	sc_membus_space;
64 	struct ppc_bus_space	sc_iobus_space;
65 	struct pcibr_config	pcibr_config;
66 	struct extent 		*sc_ioex;
67 	struct extent		*sc_memex;
68 	char			sc_ioex_name[32];
69 	char			sc_memex_name[32];
70 };
71 
72 struct cfattach mpcpcibr_ca = {
73         sizeof(struct pcibr_softc), mpcpcibrmatch, mpcpcibrattach,
74 };
75 
76 struct cfdriver mpcpcibr_cd = {
77 	NULL, "mpcpcibr", DV_DULL,
78 };
79 
80 static int      mpcpcibrprint(void *, const char *pnp);
81 
82 void	mpcpcibus_find_ranges_32(struct pcibr_softc *, u_int32_t *, int);
83 void	mpcpcibus_find_ranges_64(struct pcibr_softc *, u_int32_t *, int);
84 
85 /*
86  * config types
87  * bit meanings
88  * 0 - standard cf8/cfc type configurations,
89  *     sometimes the base addresses for these are different
90  * 1 - Config Method #2 configuration - uni-north
91  *
92  * 2 - 64 bit config bus, data for accesses &4 is at daddr+4;
93  */
94 struct config_type{
95 	char * compat;
96 	u_int32_t addr;	/* offset */
97 	u_int32_t data;	/* offset */
98 	int config_type;
99 };
100 struct config_type config_offsets[] = {
101 	{"grackle",		0x00c00cf8, 0x00e00cfc, 0 },
102 	{"bandit",		0x00800000, 0x00c00000, 1 },
103 	{"uni-north",		0x00800000, 0x00c00000, 3 },
104 	{"u3-agp",		0x00800000, 0x00c00000, 3 },
105 	{"u3-ht",		0x00000cf8, 0x00000cfc, 3 },
106 	{"legacy",		0x00000cf8, 0x00000cfc, 0 },
107 	{"IBM,27-82660",	0x00000cf8, 0x00000cfc, 0 },
108 	{NULL,			0x00000000, 0x00000000, 0 },
109 };
110 
111 int
112 mpcpcibrmatch(struct device *parent, void *match, void *aux)
113 {
114 	struct confargs *ca = aux;
115 	int found = 0;
116 
117 	if (strcmp(ca->ca_name, mpcpcibr_cd.cd_name) != 0)
118 		return (found);
119 
120 	found = 1;
121 
122 	return found;
123 }
124 
125 struct ranges_32 {
126 	u_int32_t cspace;
127 	u_int32_t child_hi;
128 	u_int32_t child_lo;
129 	u_int32_t phys;
130 	u_int32_t size_hi;
131 	u_int32_t size_lo;
132 };
133 
134 void
135 mpcpcibus_find_ranges_32(struct pcibr_softc *sc, u_int32_t *range_store,
136     int rangesize)
137 {
138 	int i, found;
139 	unsigned int base = 0;
140 	unsigned int size = 0;
141 	struct ranges_32 *prange = (void *)range_store;
142 	int rangelen;
143 
144 	rangelen = rangesize / sizeof(struct ranges_32);
145 
146 	/* mac configs */
147 	sc->sc_membus_space.bus_base = 0;
148 	sc->sc_membus_space.bus_io = 0;
149 	sc->sc_iobus_space.bus_base = 0;
150 	sc->sc_iobus_space.bus_io = 1;
151 
152 	/* find io(config) base, flag == 0x01000000 */
153 	found = 0;
154 	for (i = 0; i < rangelen; i++) {
155 		if (prange[i].cspace == 0x01000000) {
156 			/* find last? */
157 			found = i;
158 
159 			if (sc->sc_ioex)
160 				extent_free(sc->sc_ioex, prange[i].child_lo,
161 				    prange[i].size_lo, EX_NOWAIT);
162 		}
163 	}
164 	/* found the io space ranges */
165 	if (prange[found].cspace == 0x01000000) {
166 		sc->sc_iobus_space.bus_base = prange[found].phys;
167 		sc->sc_iobus_space.bus_size = prange[found].size_lo;
168 	}
169 
170 	/* the mem space ranges
171 	 * apple openfirmware always puts full
172 	 * addresses in config information,
173 	 * it is not necessary to have correct bus
174 	 * base address, but since 0 is reserved
175 	 * and all IO and device memory will be in
176 	 * upper 2G of address space, set to
177 	 * 0x80000000
178 	 */
179 	for (i = 0; i < rangelen; i++) {
180 		if (prange[i].cspace == 0x02000000) {
181 #ifdef DEBUG_PCI
182 			printf("\nfound mem %x %x",
183 				prange[i].phys,
184 				prange[i].size_lo);
185 #endif
186 			if (base != 0) {
187 				if ((base + size) == prange[i].phys)
188 					size += prange[i].size_lo;
189 				else {
190 					base = prange[i].phys;
191 					size = prange[i].size_lo;
192 				}
193 			} else {
194 				base = prange[i].phys;
195 				size = prange[i].size_lo;
196 			}
197 
198 			if (sc->sc_memex)
199 				extent_free(sc->sc_memex, prange[i].child_lo,
200 				    prange[i].size_lo, EX_NOWAIT);
201 		}
202 	}
203 	sc->sc_membus_space.bus_base = base;
204 	sc->sc_membus_space.bus_size = size;
205 }
206 
207 struct ranges_64 {
208 	u_int32_t cspace;
209 	u_int32_t child_hi;
210 	u_int32_t child_lo;
211 	u_int32_t phys_hi;
212 	u_int32_t phys_lo;
213 	u_int32_t size_hi;
214 	u_int32_t size_lo;
215 };
216 
217 void
218 mpcpcibus_find_ranges_64(struct pcibr_softc *sc, u_int32_t *range_store,
219     int rangesize)
220 {
221 	int i, found;
222 	unsigned int base = 0;
223 	unsigned int size = 0;
224 	struct ranges_64 *prange = (void *)range_store;
225 	int rangelen;
226 
227 	rangelen = rangesize / sizeof(struct ranges_64);
228 
229 	/* mac configs */
230 	sc->sc_membus_space.bus_base = 0;
231 	sc->sc_membus_space.bus_io = 0;
232 	sc->sc_iobus_space.bus_base = 0;
233 	sc->sc_iobus_space.bus_io = 1;
234 
235 	if (prange[0].cspace == 0xabb10113) { /* appl U3; */
236 		prange[0].cspace = 0x01000000;
237 		prange[0].child_lo = 0x00000000;
238 		prange[0].phys_lo = 0xf8070000;
239 		prange[0].size_lo = 0x00001000;
240 		prange[1].cspace = 0x02000000;
241 		prange[1].child_lo = 0xf2000000;
242 		prange[1].phys_lo = 0xf2000000;
243 		prange[1].size_lo = 0x02800000;
244 		rangelen = 2;
245 	}
246 
247 	/* find io(config) base, flag == 0x01000000 */
248 	found = 0;
249 	for (i = 0; i < rangelen; i++) {
250 		if (prange[i].cspace == 0x01000000) {
251 			/* find last? */
252 			found = i;
253 
254 			if (sc->sc_ioex)
255 				extent_free(sc->sc_ioex, prange[i].child_lo,
256 				    prange[i].size_lo, EX_NOWAIT);
257 		}
258 	}
259 	/* found the io space ranges */
260 	if (prange[found].cspace == 0x01000000) {
261 		sc->sc_iobus_space.bus_base = prange[found].phys_lo;
262 		sc->sc_iobus_space.bus_size = prange[found].size_lo;
263 	}
264 
265 	/* the mem space ranges
266 	 * apple openfirmware always puts full
267 	 * addresses in config information,
268 	 * it is not necessary to have correct bus
269 	 * base address, but since 0 is reserved
270 	 * and all IO and device memory will be in
271 	 * upper 2G of address space, set to
272 	 * 0x80000000
273 	 */
274 	for (i = 0; i < rangelen; i++) {
275 		if (prange[i].cspace == 0x02000000) {
276 #ifdef DEBUG_PCI
277 			printf("\nfound mem %x %x",
278 				prange[i].phys_lo,
279 				prange[i].size_lo);
280 #endif
281 			if (base != 0) {
282 				if ((base + size) == prange[i].phys_lo) {
283 					size += prange[i].size_lo;
284 				} else {
285 					base = prange[i].phys_lo;
286 					size = prange[i].size_lo;
287 				}
288 			} else {
289 				base = prange[i].phys_lo;
290 				size = prange[i].size_lo;
291 			}
292 
293 			if (sc->sc_memex)
294 				extent_free(sc->sc_memex, prange[i].child_lo,
295 				    prange[i].size_lo, EX_NOWAIT);
296 		}
297 	}
298 	sc->sc_membus_space.bus_base = base;
299 	sc->sc_membus_space.bus_size = size;
300 }
301 
302 void
303 mpcpcibrattach(struct device *parent, struct device *self, void *aux)
304 {
305 	struct pcibr_softc *sc = (struct pcibr_softc *)self;
306 	struct confargs *ca = aux;
307 	struct pcibr_config *lcp;
308 	struct pcibus_attach_args pba;
309 	int of_node = 0;
310 	char compat[32];
311 	u_int32_t addr_offset;
312 	u_int32_t data_offset;
313 	int i;
314 	int len;
315 	int rangesize;
316 	u_int32_t range_store[32];
317 
318 	if (ca->ca_node == 0) {
319 		printf("invalid node on mpcpcibr config\n");
320 		return;
321 	}
322 	len=OF_getprop(ca->ca_node, "name", compat, sizeof (compat));
323 	compat[len] = '\0';
324 	if (len > 0)
325 		printf(" %s", compat);
326 
327 	len=OF_getprop(ca->ca_node, "compatible", compat,
328 	    sizeof (compat));
329 	if (len <= 0 ) {
330 		len=OF_getprop(ca->ca_node, "name", compat,
331 			sizeof (compat));
332 		if (len <= 0) {
333 			printf(" compatible and name not found\n");
334 			return;
335 		}
336 		compat[len] = 0;
337 		if (strcmp (compat, "bandit") != 0) {
338 			printf(" compatible not found and name %s found\n",
339 			    compat);
340 			return;
341 		}
342 	}
343 	compat[len] = 0;
344 	if ((rangesize = OF_getprop(ca->ca_node, "ranges",
345 	    range_store, sizeof (range_store))) <= 0) {
346 		if (strcmp(compat, "u3-ht") == 0) {
347 			range_store[0] = 0xabb10113; /* appl U3; */
348 		} else
349 			printf("range lookup failed, node %x\n", ca->ca_node);
350 	}
351 	/* translate byte(s) into item count*/
352 
353 	lcp = &sc->pcibr_config;
354 
355 	snprintf(sc->sc_ioex_name, sizeof(sc->sc_ioex_name),
356 	    "%s pciio", sc->sc_dev.dv_xname);
357 	sc->sc_ioex = extent_create(sc->sc_ioex_name, 0x00000000, 0xffffffff,
358 	    M_DEVBUF, NULL, 0, EX_NOWAIT | EX_FILLED);
359 	snprintf(sc->sc_memex_name, sizeof(sc->sc_memex_name),
360 	    "%s pcimem", sc->sc_dev.dv_xname);
361 	sc->sc_memex = extent_create(sc->sc_memex_name, 0x00000000, 0xffffffff,
362 	    M_DEVBUF, NULL, 0, EX_NOWAIT | EX_FILLED);
363 
364 	if (ppc_proc_is_64b)
365 		mpcpcibus_find_ranges_64 (sc, range_store, rangesize);
366 	else
367 		mpcpcibus_find_ranges_32 (sc, range_store, rangesize);
368 
369 	addr_offset = 0;
370 	for (i = 0; config_offsets[i].compat != NULL; i++) {
371 		struct config_type *co = &config_offsets[i];
372 		if (strcmp(co->compat, compat) == 0) {
373 			addr_offset = co->addr;
374 			data_offset = co->data;
375 			lcp->config_type = co->config_type;
376 			break;
377 		}
378 	}
379 	if (addr_offset == 0) {
380 		printf("unable to find match for"
381 		    " compatible %s\n", compat);
382 		return;
383 	}
384 #ifdef DEBUG_FIXUP
385 	printf(" mem base %x sz %x io base %x sz %x\n"
386 	    " config addr %x config data %x\n",
387 	    sc->sc_membus_space.bus_base,
388 	    sc->sc_membus_space.bus_size,
389 	    sc->sc_iobus_space.bus_base,
390 	    sc->sc_iobus_space.bus_size,
391 	    addr_offset, data_offset);
392 #endif
393 
394 	if ( bus_space_map(&(sc->sc_iobus_space), addr_offset,
395 		NBPG, 0, &lcp->ioh_cf8) != 0 )
396 		panic("mpcpcibus: unable to map self");
397 
398 	if ( bus_space_map(&(sc->sc_iobus_space), data_offset,
399 		NBPG, 0, &lcp->ioh_cfc) != 0 )
400 		panic("mpcpcibus: unable to map self");
401 
402 	of_node = ca->ca_node;
403 
404 	lcp->lc_pc.pc_conf_v = lcp;
405 	lcp->lc_pc.pc_node = ca->ca_node;
406 	lcp->lc_pc.pc_conf_read = mpc_conf_read;
407 	lcp->lc_pc.pc_conf_write = mpc_conf_write;
408 	lcp->lc_iot = &sc->sc_iobus_space;
409 	lcp->lc_memt = &sc->sc_membus_space;
410 
411 	printf(": %s\n", compat);
412 
413 	bzero(&pba, sizeof(pba));
414 	pba.pba_dmat = &pci_bus_dma_tag;
415 
416 	pba.pba_busname = "pci";
417 	pba.pba_iot = &sc->sc_iobus_space;
418 	pba.pba_memt = &sc->sc_membus_space;
419 	pba.pba_ioex = sc->sc_ioex;
420 	pba.pba_memex = sc->sc_memex;
421 	pba.pba_pc = &lcp->lc_pc;
422 	pba.pba_domain = pci_ndomains++;
423 	pba.pba_bus = 0;
424 
425 	config_found(self, &pba, mpcpcibrprint);
426 }
427 
428 static int
429 mpcpcibrprint(void *aux, const char *pnp)
430 {
431 	struct pcibus_attach_args *pba = aux;
432 
433 	if (pnp)
434 		printf("%s at %s", pba->pba_busname, pnp);
435 	printf(" bus %d", pba->pba_bus);
436 	return(UNCONF);
437 }
438 
439 u_int32_t
440 mpc_gen_config_reg(void *cpv, pcitag_t tag, int offset)
441 {
442 	struct pcibr_config *cp = cpv;
443 	unsigned int bus, dev, fcn;
444 	u_int32_t reg, val = PCITAG_OFFSET(tag);
445 
446 	pci_decompose_tag(cpv, tag, &bus, &dev, &fcn);
447 
448 	if (cp->config_type & 1) {
449 		/* Config Mechanism #2 */
450 		if (bus == 0) {
451 			if (dev < 11)
452 				return 0xffffffff;
453 			/*
454 			 * Need to do config type 0 operation
455 			 *  1 << (11?+dev) | fcn << 8 | reg
456 			 * 11? is because pci spec states
457 			 * that 11-15 is reserved.
458 			 */
459 			reg = 1 << (dev) | fcn << 8 | offset;
460 		} else {
461 			if (dev > 15)
462 				return 0xffffffff;
463 			/*
464 			 * config type 1
465 			 */
466 			reg = val | offset | 1;
467 		}
468 	} else {
469 		/* config mechanism #2, type 0
470 		 * standard cf8/cfc config
471 		 */
472 		reg =  0x80000000 | val | offset;
473 	}
474 
475 	return reg;
476 }
477 
478 /* #define DEBUG_CONFIG  */
479 pcireg_t
480 mpc_conf_read(void *cpv, pcitag_t tag, int offset)
481 {
482 	struct pcibr_config *cp = cpv;
483 	pcireg_t data;
484 	u_int32_t reg;
485 	int s;
486 	int daddr = 0;
487 	faultbuf env;
488 	void *oldh;
489 
490 	if (offset & 3 ||
491 	    offset < 0 || offset >= PCI_CONFIG_SPACE_SIZE) {
492 #ifdef DEBUG_CONFIG
493 		printf ("pci_conf_read: bad reg %x\n", offset);
494 #endif /* DEBUG_CONFIG */
495 		return(~0);
496 	}
497 
498 	reg = mpc_gen_config_reg(cpv, tag, offset);
499 	/* if invalid tag, return -1 */
500 	if (reg == 0xffffffff)
501 		return(~0);
502 
503 	if ((cp->config_type & 2) && (offset & 0x04))
504 		daddr += 4;
505 
506 	s = splhigh();
507 
508 	oldh = curpcb->pcb_onfault;
509 	if (setfault(&env)) {
510 		/* we faulted during the read? */
511 		curpcb->pcb_onfault = oldh;
512 		splx(s);
513 		return 0xffffffff;
514 	}
515 
516 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, reg);
517 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
518 	data = bus_space_read_4(cp->lc_iot, cp->ioh_cfc, daddr);
519 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, 0); /* disable */
520 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
521 
522 	curpcb->pcb_onfault = oldh;
523 
524 	splx(s);
525 #ifdef DEBUG_CONFIG
526 	if (!((offset == 0) && (data == 0xffffffff))) {
527 		unsigned int bus, dev, fcn;
528 		pci_decompose_tag(cpv, tag, &bus, &dev, &fcn);
529 		printf("mpc_conf_read bus %x dev %x fcn %x offset %x", bus, dev, fcn,
530 			offset);
531 		printf(" daddr %x reg %x",daddr, reg);
532 		printf(" data %x\n", data);
533 	}
534 #endif
535 
536 	return(data);
537 }
538 
539 void
540 mpc_conf_write(void *cpv, pcitag_t tag, int offset, pcireg_t data)
541 {
542 	struct pcibr_config *cp = cpv;
543 	u_int32_t reg;
544 	int s;
545 	int daddr = 0;
546 
547 	reg = mpc_gen_config_reg(cpv, tag, offset);
548 
549 	/* if invalid tag, return ??? */
550 	if (reg == 0xffffffff)
551 		return;
552 
553 	if ((cp->config_type & 2) && (offset & 0x04))
554 		daddr += 4;
555 
556 #ifdef DEBUG_CONFIG
557 	{
558 		unsigned int bus, dev, fcn;
559 		pci_decompose_tag(cpv, tag, &bus, &dev, &fcn);
560 		printf("mpc_conf_write bus %x dev %x fcn %x offset %x", bus,
561 			dev, fcn, offset);
562 		printf(" daddr %x reg %x",daddr, reg);
563 		printf(" data %x\n", data);
564 	}
565 #endif
566 
567 	s = splhigh();
568 
569 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, reg);
570 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
571 	bus_space_write_4(cp->lc_iot, cp->ioh_cfc, daddr, data);
572 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, 0); /* disable */
573 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
574 
575 	splx(s);
576 }
577