xref: /openbsd/sys/arch/macppc/pci/mpcpcibus.c (revision 89ed722c)
1 /*	$OpenBSD: mpcpcibus.c,v 1.49 2022/03/13 12:33:01 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 const 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 	{"u4-pcie",		0x00800000, 0x00c00000, 7 },
107 	{"legacy",		0x00000cf8, 0x00000cfc, 0 },
108 	{"IBM,27-82660",	0x00000cf8, 0x00000cfc, 0 },
109 	{NULL,			0x00000000, 0x00000000, 0 },
110 };
111 
112 int
mpcpcibrmatch(struct device * parent,void * match,void * aux)113 mpcpcibrmatch(struct device *parent, void *match, void *aux)
114 {
115 	struct confargs *ca = aux;
116 	int found = 0;
117 
118 	if (strcmp(ca->ca_name, mpcpcibr_cd.cd_name) != 0)
119 		return (found);
120 
121 	found = 1;
122 
123 	return found;
124 }
125 
126 struct ranges_32 {
127 	u_int32_t cspace;
128 	u_int32_t child_hi;
129 	u_int32_t child_lo;
130 	u_int32_t phys;
131 	u_int32_t size_hi;
132 	u_int32_t size_lo;
133 };
134 
135 void
mpcpcibus_find_ranges_32(struct pcibr_softc * sc,u_int32_t * range_store,int rangesize)136 mpcpcibus_find_ranges_32(struct pcibr_softc *sc, u_int32_t *range_store,
137     int rangesize)
138 {
139 	int i, found;
140 	unsigned int base = 0;
141 	unsigned int size = 0;
142 	struct ranges_32 *prange = (void *)range_store;
143 	int rangelen;
144 
145 	rangelen = rangesize / sizeof(struct ranges_32);
146 
147 	/* mac configs */
148 	sc->sc_membus_space.bus_base = 0;
149 	sc->sc_membus_space.bus_io = 0;
150 	sc->sc_iobus_space.bus_base = 0;
151 	sc->sc_iobus_space.bus_io = 1;
152 
153 	/* find io(config) base, flag == 0x01000000 */
154 	found = 0;
155 	for (i = 0; i < rangelen; i++) {
156 		if (prange[i].cspace == 0x01000000) {
157 			/* find last? */
158 			found = i;
159 
160 			if (sc->sc_ioex)
161 				extent_free(sc->sc_ioex, prange[i].child_lo,
162 				    prange[i].size_lo, EX_NOWAIT);
163 		}
164 	}
165 	/* found the io space ranges */
166 	if (prange[found].cspace == 0x01000000) {
167 		sc->sc_iobus_space.bus_base = prange[found].phys;
168 		sc->sc_iobus_space.bus_size = prange[found].size_lo;
169 	}
170 
171 	/* the mem space ranges
172 	 * apple openfirmware always puts full
173 	 * addresses in config information,
174 	 * it is not necessary to have correct bus
175 	 * base address, but since 0 is reserved
176 	 * and all IO and device memory will be in
177 	 * upper 2G of address space, set to
178 	 * 0x80000000
179 	 */
180 	for (i = 0; i < rangelen; i++) {
181 		if (prange[i].cspace == 0x02000000) {
182 #ifdef DEBUG_PCI
183 			printf("\nfound mem %x %x",
184 				prange[i].phys,
185 				prange[i].size_lo);
186 #endif
187 			if (base != 0) {
188 				if ((base + size) == prange[i].phys)
189 					size += prange[i].size_lo;
190 				else {
191 					base = prange[i].phys;
192 					size = prange[i].size_lo;
193 				}
194 			} else {
195 				base = prange[i].phys;
196 				size = prange[i].size_lo;
197 			}
198 
199 			if (sc->sc_memex)
200 				extent_free(sc->sc_memex, prange[i].child_lo,
201 				    prange[i].size_lo, EX_NOWAIT);
202 		}
203 	}
204 	sc->sc_membus_space.bus_base = base;
205 	sc->sc_membus_space.bus_size = size;
206 }
207 
208 struct ranges_64 {
209 	u_int32_t cspace;
210 	u_int32_t child_hi;
211 	u_int32_t child_lo;
212 	u_int32_t phys_hi;
213 	u_int32_t phys_lo;
214 	u_int32_t size_hi;
215 	u_int32_t size_lo;
216 };
217 
218 void
mpcpcibus_find_ranges_64(struct pcibr_softc * sc,u_int32_t * range_store,int rangesize)219 mpcpcibus_find_ranges_64(struct pcibr_softc *sc, u_int32_t *range_store,
220     int rangesize)
221 {
222 	int i, found;
223 	unsigned int base = 0;
224 	unsigned int size = 0;
225 	struct ranges_64 *prange = (void *)range_store;
226 	int rangelen;
227 
228 	rangelen = rangesize / sizeof(struct ranges_64);
229 
230 	/* mac configs */
231 	sc->sc_membus_space.bus_base = 0;
232 	sc->sc_membus_space.bus_io = 0;
233 	sc->sc_iobus_space.bus_base = 0;
234 	sc->sc_iobus_space.bus_io = 1;
235 
236 	if (prange[0].cspace == 0xabb10113) { /* appl U3; */
237 		prange[0].cspace = 0x01000000;
238 		prange[0].child_lo = 0x00000000;
239 		prange[0].phys_lo = 0xf8070000;
240 		prange[0].size_lo = 0x00001000;
241 		prange[1].cspace = 0x02000000;
242 		prange[1].child_lo = 0xf2000000;
243 		prange[1].phys_lo = 0xf2000000;
244 		prange[1].size_lo = 0x02800000;
245 		rangelen = 2;
246 	}
247 
248 	/* find io(config) base, flag == 0x01000000 */
249 	found = 0;
250 	for (i = 0; i < rangelen; i++) {
251 		if (prange[i].cspace == 0x01000000) {
252 			/* find last? */
253 			found = i;
254 
255 			if (sc->sc_ioex)
256 				extent_free(sc->sc_ioex, prange[i].child_lo,
257 				    prange[i].size_lo, EX_NOWAIT);
258 		}
259 	}
260 	/* found the io space ranges */
261 	if (prange[found].cspace == 0x01000000) {
262 		sc->sc_iobus_space.bus_base = prange[found].phys_lo;
263 		sc->sc_iobus_space.bus_size = prange[found].size_lo;
264 	}
265 
266 	/* the mem space ranges
267 	 * apple openfirmware always puts full
268 	 * addresses in config information,
269 	 * it is not necessary to have correct bus
270 	 * base address, but since 0 is reserved
271 	 * and all IO and device memory will be in
272 	 * upper 2G of address space, set to
273 	 * 0x80000000
274 	 */
275 	for (i = 0; i < rangelen; i++) {
276 		if (prange[i].cspace == 0x02000000) {
277 #ifdef DEBUG_PCI
278 			printf("\nfound mem %x %x",
279 				prange[i].phys_lo,
280 				prange[i].size_lo);
281 #endif
282 			if (base != 0) {
283 				if ((base + size) == prange[i].phys_lo) {
284 					size += prange[i].size_lo;
285 				} else {
286 					base = prange[i].phys_lo;
287 					size = prange[i].size_lo;
288 				}
289 			} else {
290 				base = prange[i].phys_lo;
291 				size = prange[i].size_lo;
292 			}
293 
294 			if (sc->sc_memex)
295 				extent_free(sc->sc_memex, prange[i].child_lo,
296 				    prange[i].size_lo, EX_NOWAIT);
297 		}
298 	}
299 	sc->sc_membus_space.bus_base = base;
300 	sc->sc_membus_space.bus_size = size;
301 }
302 
303 void
mpcpcibrattach(struct device * parent,struct device * self,void * aux)304 mpcpcibrattach(struct device *parent, struct device *self, void *aux)
305 {
306 	struct pcibr_softc *sc = (struct pcibr_softc *)self;
307 	struct confargs *ca = aux;
308 	struct pcibr_config *lcp;
309 	struct pcibus_attach_args pba;
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 	int busrange[2];
318 
319 	if (ca->ca_node == 0) {
320 		printf("invalid node on mpcpcibr config\n");
321 		return;
322 	}
323 	len = OF_getprop(ca->ca_node, "name", compat, sizeof(compat));
324 	compat[len] = '\0';
325 	if (len > 0)
326 		printf(" %s", compat);
327 
328 	len = OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat));
329 	if (len <= 0) {
330 		len = OF_getprop(ca->ca_node, "name", compat, sizeof(compat));
331 		if (len <= 0) {
332 			printf(" compatible and name not found\n");
333 			return;
334 		}
335 		compat[len] = 0;
336 		if (strcmp(compat, "bandit") != 0) {
337 			printf(" compatible not found and name %s found\n",
338 			    compat);
339 			return;
340 		}
341 	}
342 	compat[len] = 0;
343 	if ((rangesize = OF_getprop(ca->ca_node, "ranges",
344 	    range_store, sizeof (range_store))) <= 0) {
345 		if (strcmp(compat, "u3-ht") == 0) {
346 			range_store[0] = 0xabb10113; /* appl U3; */
347 		} else
348 			printf("range lookup failed, node %x\n", ca->ca_node);
349 	}
350 
351 	lcp = &sc->pcibr_config;
352 
353 	snprintf(sc->sc_ioex_name, sizeof(sc->sc_ioex_name),
354 	    "%s pciio", sc->sc_dev.dv_xname);
355 	sc->sc_ioex = extent_create(sc->sc_ioex_name, 0x00000000, 0xffffffff,
356 	    M_DEVBUF, NULL, 0, EX_NOWAIT | EX_FILLED);
357 	snprintf(sc->sc_memex_name, sizeof(sc->sc_memex_name),
358 	    "%s pcimem", sc->sc_dev.dv_xname);
359 	sc->sc_memex = extent_create(sc->sc_memex_name, 0x00000000, 0xffffffff,
360 	    M_DEVBUF, NULL, 0, EX_NOWAIT | EX_FILLED);
361 
362 	if (ppc_proc_is_64b)
363 		mpcpcibus_find_ranges_64(sc, range_store, rangesize);
364 	else
365 		mpcpcibus_find_ranges_32(sc, range_store, rangesize);
366 
367 	addr_offset = 0;
368 	for (i = 0; config_offsets[i].compat != NULL; i++) {
369 		struct config_type *co = &config_offsets[i];
370 		if (strcmp(co->compat, compat) == 0) {
371 			addr_offset = co->addr;
372 			data_offset = co->data;
373 			lcp->config_type = co->config_type;
374 			break;
375 		}
376 	}
377 	if (addr_offset == 0) {
378 		printf("unable to find match for"
379 		    " compatible %s\n", compat);
380 		return;
381 	}
382 #ifdef DEBUG_FIXUP
383 	printf(" mem base %x sz %x io base %x sz %x\n"
384 	    " config addr %x config data %x\n",
385 	    sc->sc_membus_space.bus_base,
386 	    sc->sc_membus_space.bus_size,
387 	    sc->sc_iobus_space.bus_base,
388 	    sc->sc_iobus_space.bus_size,
389 	    addr_offset, data_offset);
390 #endif
391 
392 	if (bus_space_map(&(sc->sc_iobus_space), addr_offset,
393 		NBPG, 0, &lcp->ioh_cf8) != 0)
394 		panic("mpcpcibus: unable to map self");
395 
396 	if (bus_space_map(&(sc->sc_iobus_space), data_offset,
397 		NBPG, 0, &lcp->ioh_cfc) != 0)
398 		panic("mpcpcibus: unable to map self");
399 
400 	lcp->lc_pc.pc_conf_v = lcp;
401 	lcp->lc_pc.pc_node = ca->ca_node;
402 	lcp->lc_pc.pc_conf_read = mpc_conf_read;
403 	lcp->lc_pc.pc_conf_write = mpc_conf_write;
404 	lcp->lc_iot = &sc->sc_iobus_space;
405 	lcp->lc_memt = &sc->sc_membus_space;
406 
407 	printf(": %s\n", compat);
408 
409 	bzero(&pba, sizeof(pba));
410 	pba.pba_dmat = &pci_bus_dma_tag;
411 
412 	pba.pba_busname = "pci";
413 	pba.pba_iot = &sc->sc_iobus_space;
414 	pba.pba_memt = &sc->sc_membus_space;
415 	pba.pba_ioex = sc->sc_ioex;
416 	pba.pba_memex = sc->sc_memex;
417 	pba.pba_pc = &lcp->lc_pc;
418 	pba.pba_domain = pci_ndomains++;
419 	if (OF_getprop(ca->ca_node, "bus-range", &busrange, sizeof(busrange)) <
420 	    0)
421 		pba.pba_bus = 0;
422 	else
423 		pba.pba_bus = busrange[0];
424 
425 	config_found(self, &pba, mpcpcibrprint);
426 }
427 
428 static int
mpcpcibrprint(void * aux,const char * pnp)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
mpc_gen_config_reg(void * cpv,pcitag_t tag,int offset)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 & 4) {
449 		reg = val | offset | 1;
450 		reg |= (offset >> 8) << 28;
451 	} else if (cp->config_type & 1) {
452 		/* Config Mechanism #2 */
453 		if (bus == 0) {
454 			if (dev < 11)
455 				return 0xffffffff;
456 			/*
457 			 * Need to do config type 0 operation
458 			 *  1 << (11?+dev) | fcn << 8 | reg
459 			 * 11? is because pci spec states
460 			 * that 11-15 is reserved.
461 			 */
462 			reg = 1 << (dev) | fcn << 8 | offset;
463 		} else {
464 			if (dev > 15)
465 				return 0xffffffff;
466 			/*
467 			 * config type 1
468 			 */
469 			reg = val | offset | 1;
470 		}
471 	} else {
472 		/* config mechanism #2, type 0
473 		 * standard cf8/cfc config
474 		 */
475 		reg =  0x80000000 | val | offset;
476 	}
477 
478 	return reg;
479 }
480 
481 /* #define DEBUG_CONFIG  */
482 pcireg_t
mpc_conf_read(void * cpv,pcitag_t tag,int offset)483 mpc_conf_read(void *cpv, pcitag_t tag, int offset)
484 {
485 	struct pcibr_config *cp = cpv;
486 	pcireg_t data;
487 	u_int32_t reg;
488 	int s;
489 	int daddr = 0;
490 	faultbuf env;
491 	void *oldh;
492 
493 	if (offset & 3 ||
494 	    offset < 0 || offset >= PCI_CONFIG_SPACE_SIZE) {
495 #ifdef DEBUG_CONFIG
496 		printf ("pci_conf_read: bad reg %x\n", offset);
497 #endif /* DEBUG_CONFIG */
498 		return(~0);
499 	}
500 
501 	reg = mpc_gen_config_reg(cpv, tag, offset);
502 	/* if invalid tag, return -1 */
503 	if (reg == 0xffffffff)
504 		return(~0);
505 
506 	if ((cp->config_type & 2) && (offset & 0x04))
507 		daddr += 4;
508 
509 	s = splhigh();
510 
511 	oldh = curpcb->pcb_onfault;
512 	if (setfault(&env)) {
513 		/* we faulted during the read? */
514 		curpcb->pcb_onfault = oldh;
515 		splx(s);
516 		return 0xffffffff;
517 	}
518 
519 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, reg);
520 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
521 	data = bus_space_read_4(cp->lc_iot, cp->ioh_cfc, daddr);
522 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, 0); /* disable */
523 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
524 
525 	curpcb->pcb_onfault = oldh;
526 
527 	splx(s);
528 #ifdef DEBUG_CONFIG
529 	if (!((offset == 0) && (data == 0xffffffff))) {
530 		unsigned int bus, dev, fcn;
531 		pci_decompose_tag(cpv, tag, &bus, &dev, &fcn);
532 		printf("mpc_conf_read bus %x dev %x fcn %x offset %x", bus, dev, fcn,
533 			offset);
534 		printf(" daddr %x reg %x",daddr, reg);
535 		printf(" data %x\n", data);
536 	}
537 #endif
538 
539 	return(data);
540 }
541 
542 void
mpc_conf_write(void * cpv,pcitag_t tag,int offset,pcireg_t data)543 mpc_conf_write(void *cpv, pcitag_t tag, int offset, pcireg_t data)
544 {
545 	struct pcibr_config *cp = cpv;
546 	u_int32_t reg;
547 	int s;
548 	int daddr = 0;
549 
550 	reg = mpc_gen_config_reg(cpv, tag, offset);
551 
552 	/* if invalid tag, return ??? */
553 	if (reg == 0xffffffff)
554 		return;
555 
556 	if ((cp->config_type & 2) && (offset & 0x04))
557 		daddr += 4;
558 
559 #ifdef DEBUG_CONFIG
560 	{
561 		unsigned int bus, dev, fcn;
562 		pci_decompose_tag(cpv, tag, &bus, &dev, &fcn);
563 		printf("mpc_conf_write bus %x dev %x fcn %x offset %x", bus,
564 			dev, fcn, offset);
565 		printf(" daddr %x reg %x",daddr, reg);
566 		printf(" data %x\n", data);
567 	}
568 #endif
569 
570 	s = splhigh();
571 
572 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, reg);
573 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
574 	bus_space_write_4(cp->lc_iot, cp->ioh_cfc, daddr, data);
575 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, 0); /* disable */
576 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
577 
578 	splx(s);
579 }
580