xref: /openbsd/sys/arch/macppc/pci/mpcpcibus.c (revision e5dd7070)
1 /*	$OpenBSD: mpcpcibus.c,v 1.47 2015/06/03 08:41:43 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 	{"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
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
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
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
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 	int of_node = 0;
311 	char compat[32];
312 	u_int32_t addr_offset;
313 	u_int32_t data_offset;
314 	int i;
315 	int len;
316 	int rangesize;
317 	u_int32_t range_store[32];
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,
329 	    sizeof (compat));
330 	if (len <= 0 ) {
331 		len=OF_getprop(ca->ca_node, "name", compat,
332 			sizeof (compat));
333 		if (len <= 0) {
334 			printf(" compatible and name not found\n");
335 			return;
336 		}
337 		compat[len] = 0;
338 		if (strcmp (compat, "bandit") != 0) {
339 			printf(" compatible not found and name %s found\n",
340 			    compat);
341 			return;
342 		}
343 	}
344 	compat[len] = 0;
345 	if ((rangesize = OF_getprop(ca->ca_node, "ranges",
346 	    range_store, sizeof (range_store))) <= 0) {
347 		if (strcmp(compat, "u3-ht") == 0) {
348 			range_store[0] = 0xabb10113; /* appl U3; */
349 		} else
350 			printf("range lookup failed, node %x\n", ca->ca_node);
351 	}
352 	/* translate byte(s) into item count*/
353 
354 	lcp = &sc->pcibr_config;
355 
356 	snprintf(sc->sc_ioex_name, sizeof(sc->sc_ioex_name),
357 	    "%s pciio", sc->sc_dev.dv_xname);
358 	sc->sc_ioex = extent_create(sc->sc_ioex_name, 0x00000000, 0xffffffff,
359 	    M_DEVBUF, NULL, 0, EX_NOWAIT | EX_FILLED);
360 	snprintf(sc->sc_memex_name, sizeof(sc->sc_memex_name),
361 	    "%s pcimem", sc->sc_dev.dv_xname);
362 	sc->sc_memex = extent_create(sc->sc_memex_name, 0x00000000, 0xffffffff,
363 	    M_DEVBUF, NULL, 0, EX_NOWAIT | EX_FILLED);
364 
365 	if (ppc_proc_is_64b)
366 		mpcpcibus_find_ranges_64 (sc, range_store, rangesize);
367 	else
368 		mpcpcibus_find_ranges_32 (sc, range_store, rangesize);
369 
370 	addr_offset = 0;
371 	for (i = 0; config_offsets[i].compat != NULL; i++) {
372 		struct config_type *co = &config_offsets[i];
373 		if (strcmp(co->compat, compat) == 0) {
374 			addr_offset = co->addr;
375 			data_offset = co->data;
376 			lcp->config_type = co->config_type;
377 			break;
378 		}
379 	}
380 	if (addr_offset == 0) {
381 		printf("unable to find match for"
382 		    " compatible %s\n", compat);
383 		return;
384 	}
385 #ifdef DEBUG_FIXUP
386 	printf(" mem base %x sz %x io base %x sz %x\n"
387 	    " config addr %x config data %x\n",
388 	    sc->sc_membus_space.bus_base,
389 	    sc->sc_membus_space.bus_size,
390 	    sc->sc_iobus_space.bus_base,
391 	    sc->sc_iobus_space.bus_size,
392 	    addr_offset, data_offset);
393 #endif
394 
395 	if ( bus_space_map(&(sc->sc_iobus_space), addr_offset,
396 		NBPG, 0, &lcp->ioh_cf8) != 0 )
397 		panic("mpcpcibus: unable to map self");
398 
399 	if ( bus_space_map(&(sc->sc_iobus_space), data_offset,
400 		NBPG, 0, &lcp->ioh_cfc) != 0 )
401 		panic("mpcpcibus: unable to map self");
402 
403 	of_node = ca->ca_node;
404 
405 	lcp->lc_pc.pc_conf_v = lcp;
406 	lcp->lc_pc.pc_node = ca->ca_node;
407 	lcp->lc_pc.pc_conf_read = mpc_conf_read;
408 	lcp->lc_pc.pc_conf_write = mpc_conf_write;
409 	lcp->lc_iot = &sc->sc_iobus_space;
410 	lcp->lc_memt = &sc->sc_membus_space;
411 
412 	printf(": %s\n", compat);
413 
414 	bzero(&pba, sizeof(pba));
415 	pba.pba_dmat = &pci_bus_dma_tag;
416 
417 	pba.pba_busname = "pci";
418 	pba.pba_iot = &sc->sc_iobus_space;
419 	pba.pba_memt = &sc->sc_membus_space;
420 	pba.pba_ioex = sc->sc_ioex;
421 	pba.pba_memex = sc->sc_memex;
422 	pba.pba_pc = &lcp->lc_pc;
423 	pba.pba_domain = pci_ndomains++;
424 	pba.pba_bus = 0;
425 
426 	config_found(self, &pba, mpcpcibrprint);
427 }
428 
429 static int
430 mpcpcibrprint(void *aux, const char *pnp)
431 {
432 	struct pcibus_attach_args *pba = aux;
433 
434 	if (pnp)
435 		printf("%s at %s", pba->pba_busname, pnp);
436 	printf(" bus %d", pba->pba_bus);
437 	return(UNCONF);
438 }
439 
440 u_int32_t
441 mpc_gen_config_reg(void *cpv, pcitag_t tag, int offset)
442 {
443 	struct pcibr_config *cp = cpv;
444 	unsigned int bus, dev, fcn;
445 	u_int32_t reg, val = PCITAG_OFFSET(tag);
446 
447 	pci_decompose_tag(cpv, tag, &bus, &dev, &fcn);
448 
449 	if (cp->config_type & 4) {
450 		reg = val | offset | 1;
451 		reg |= (offset >> 8) << 28;
452 	} else if (cp->config_type & 1) {
453 		/* Config Mechanism #2 */
454 		if (bus == 0) {
455 			if (dev < 11)
456 				return 0xffffffff;
457 			/*
458 			 * Need to do config type 0 operation
459 			 *  1 << (11?+dev) | fcn << 8 | reg
460 			 * 11? is because pci spec states
461 			 * that 11-15 is reserved.
462 			 */
463 			reg = 1 << (dev) | fcn << 8 | offset;
464 		} else {
465 			if (dev > 15)
466 				return 0xffffffff;
467 			/*
468 			 * config type 1
469 			 */
470 			reg = val | offset | 1;
471 		}
472 	} else {
473 		/* config mechanism #2, type 0
474 		 * standard cf8/cfc config
475 		 */
476 		reg =  0x80000000 | val | offset;
477 	}
478 
479 	return reg;
480 }
481 
482 /* #define DEBUG_CONFIG  */
483 pcireg_t
484 mpc_conf_read(void *cpv, pcitag_t tag, int offset)
485 {
486 	struct pcibr_config *cp = cpv;
487 	pcireg_t data;
488 	u_int32_t reg;
489 	int s;
490 	int daddr = 0;
491 	faultbuf env;
492 	void *oldh;
493 
494 	if (offset & 3 ||
495 	    offset < 0 || offset >= PCI_CONFIG_SPACE_SIZE) {
496 #ifdef DEBUG_CONFIG
497 		printf ("pci_conf_read: bad reg %x\n", offset);
498 #endif /* DEBUG_CONFIG */
499 		return(~0);
500 	}
501 
502 	reg = mpc_gen_config_reg(cpv, tag, offset);
503 	/* if invalid tag, return -1 */
504 	if (reg == 0xffffffff)
505 		return(~0);
506 
507 	if ((cp->config_type & 2) && (offset & 0x04))
508 		daddr += 4;
509 
510 	s = splhigh();
511 
512 	oldh = curpcb->pcb_onfault;
513 	if (setfault(&env)) {
514 		/* we faulted during the read? */
515 		curpcb->pcb_onfault = oldh;
516 		splx(s);
517 		return 0xffffffff;
518 	}
519 
520 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, reg);
521 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
522 	data = bus_space_read_4(cp->lc_iot, cp->ioh_cfc, daddr);
523 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, 0); /* disable */
524 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
525 
526 	curpcb->pcb_onfault = oldh;
527 
528 	splx(s);
529 #ifdef DEBUG_CONFIG
530 	if (!((offset == 0) && (data == 0xffffffff))) {
531 		unsigned int bus, dev, fcn;
532 		pci_decompose_tag(cpv, tag, &bus, &dev, &fcn);
533 		printf("mpc_conf_read bus %x dev %x fcn %x offset %x", bus, dev, fcn,
534 			offset);
535 		printf(" daddr %x reg %x",daddr, reg);
536 		printf(" data %x\n", data);
537 	}
538 #endif
539 
540 	return(data);
541 }
542 
543 void
544 mpc_conf_write(void *cpv, pcitag_t tag, int offset, pcireg_t data)
545 {
546 	struct pcibr_config *cp = cpv;
547 	u_int32_t reg;
548 	int s;
549 	int daddr = 0;
550 
551 	reg = mpc_gen_config_reg(cpv, tag, offset);
552 
553 	/* if invalid tag, return ??? */
554 	if (reg == 0xffffffff)
555 		return;
556 
557 	if ((cp->config_type & 2) && (offset & 0x04))
558 		daddr += 4;
559 
560 #ifdef DEBUG_CONFIG
561 	{
562 		unsigned int bus, dev, fcn;
563 		pci_decompose_tag(cpv, tag, &bus, &dev, &fcn);
564 		printf("mpc_conf_write bus %x dev %x fcn %x offset %x", bus,
565 			dev, fcn, offset);
566 		printf(" daddr %x reg %x",daddr, reg);
567 		printf(" data %x\n", data);
568 	}
569 #endif
570 
571 	s = splhigh();
572 
573 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, reg);
574 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
575 	bus_space_write_4(cp->lc_iot, cp->ioh_cfc, daddr, data);
576 	bus_space_write_4(cp->lc_iot, cp->ioh_cf8, 0, 0); /* disable */
577 	bus_space_read_4(cp->lc_iot, cp->ioh_cf8, 0); /* XXX */
578 
579 	splx(s);
580 }
581