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