xref: /openbsd-src/sys/dev/pci/sti_pci.c (revision 0b21a9cf7938dc519e6a8238d2ca9d6221665cea)
1 /*	$OpenBSD: sti_pci.c,v 1.14 2024/08/17 08:45:22 miod Exp $	*/
2 
3 /*
4  * Copyright (c) 2006, 2007, 2023 Miodrag Vallat.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice, this permission notice, and the disclaimer below
9  * appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/device.h>
23 
24 #include <dev/pci/pcireg.h>
25 #include <dev/pci/pcivar.h>
26 #include <dev/pci/pcidevs.h>
27 
28 #include <dev/wscons/wsdisplayvar.h>
29 
30 #include <dev/ic/stireg.h>
31 #include <dev/ic/stivar.h>
32 
33 int	sti_pci_match(struct device *, void *, void *);
34 void	sti_pci_attach(struct device *, struct device *, void *);
35 
36 struct	sti_pci_softc {
37 	struct sti_softc	sc_base;
38 
39 	pci_chipset_tag_t	sc_pc;
40 	pcitag_t		sc_tag;
41 
42 	bus_space_handle_t	sc_romh;
43 };
44 
45 const struct cfattach sti_pci_ca = {
46 	sizeof(struct sti_pci_softc), sti_pci_match, sti_pci_attach
47 };
48 
49 const struct pci_matchid sti_pci_devices[] = {
50 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_EG },
51 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX2 },
52 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX4 },
53 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX6 },
54 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FXE },
55 #ifdef notyet
56 	{ PCI_VENDOR_IBM, PCI_PRODUCT_IBM_FIREGL2 }
57 #endif
58 };
59 
60 int	sti_readbar(struct sti_softc *, struct pci_attach_args *, u_int, int);
61 int	sti_check_rom(struct sti_pci_softc *, struct pci_attach_args *);
62 void	sti_pci_enable_rom(struct sti_softc *);
63 void	sti_pci_disable_rom(struct sti_softc *);
64 
65 int	sti_pci_is_console(struct pci_attach_args *, bus_addr_t *);
66 
67 int
68 sti_pci_match(struct device *parent, void *cf, void *aux)
69 {
70 	struct pci_attach_args *paa = aux;
71 
72 	return pci_matchbyid(paa, sti_pci_devices,
73 	    sizeof(sti_pci_devices) / sizeof(sti_pci_devices[0]));
74 }
75 
76 void
77 sti_pci_attach(struct device *parent, struct device *self, void *aux)
78 {
79 	struct sti_pci_softc *spc = (void *)self;
80 	struct pci_attach_args *paa = aux;
81 
82 	spc->sc_pc = paa->pa_pc;
83 	spc->sc_tag = paa->pa_tag;
84 	spc->sc_base.sc_enable_rom = sti_pci_enable_rom;
85 	spc->sc_base.sc_disable_rom = sti_pci_disable_rom;
86 
87 	printf("\n");
88 
89 	if (sti_check_rom(spc, paa) != 0)
90 		return;
91 
92 	printf("%s", self->dv_xname);
93 	if (sti_pci_is_console(paa, spc->sc_base.bases) != 0)
94 		spc->sc_base.sc_flags |= STI_CONSOLE;
95 	if (sti_attach_common(&spc->sc_base, paa->pa_iot, paa->pa_memt,
96 	    spc->sc_romh, STI_CODEBASE_MAIN) == 0)
97 		startuphook_establish(sti_end_attach, spc);
98 }
99 
100 /*
101  * Enable PCI ROM.
102  */
103 void
104 sti_pci_enable_rom(struct sti_softc *sc)
105 {
106 	struct sti_pci_softc *spc = (struct sti_pci_softc *)sc;
107 	pcireg_t address;
108 
109 	if (!ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
110 		address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_ROM_REG);
111 		address |= PCI_ROM_ENABLE;
112 		pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_ROM_REG, address);
113 		SET(sc->sc_flags, STI_ROM_ENABLED);
114 	}
115 }
116 
117 /*
118  * Disable PCI ROM.
119  */
120 void
121 sti_pci_disable_rom(struct sti_softc *sc)
122 {
123 	struct sti_pci_softc *spc = (struct sti_pci_softc *)sc;
124 	pcireg_t address;
125 
126 	if (ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
127 		address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_ROM_REG);
128 		address &= ~PCI_ROM_ENABLE;
129 		pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_ROM_REG, address);
130 
131 		CLR(sc->sc_flags, STI_ROM_ENABLED);
132 	}
133 }
134 
135 /*
136  * We have to be extremely careful with output in this file, as the
137  * device we are trying to attach might be the console, and we are
138  * still using the PDC routines for output at this point.
139  *
140  * On some devices, if not all, PDC routines assume the STI ROM is *NOT*
141  * mapped when they are invoked, and they will cause the system to freeze
142  * if it is mapped.
143  *
144  * As a result, we need to make sure the ROM is not mapped when invoking
145  * printf(). The following wrapper takes care of this to reduce the risk
146  * of making a mistake.
147  */
148 
149 static int
150 sti_local_printf(struct sti_softc *sc, const char *fmt, ...)
151 {
152 	va_list ap;
153 	int rc;
154 	int enabled = sc->sc_flags & STI_ROM_ENABLED;
155 
156 	if (enabled)
157 		sti_pci_disable_rom(sc);
158 	va_start(ap, fmt);
159 	rc = vprintf(fmt, ap);
160 	if (enabled)
161 		sti_pci_enable_rom(sc);
162 
163 	return rc;
164 }
165 
166 #define printf(fmt, ...) sti_local_printf(sc, fmt, ## __VA_ARGS__)
167 
168 /*
169  * PCI ROM Data Structure (6.3.1.2)
170  */
171 
172 struct pcirom_ds {
173 	uint32_t signature;
174 	uint16_t vid;
175 	uint16_t pid;
176 	uint16_t reserved_8;
177 	uint16_t dslen;
178 	uint8_t rev;
179 	uint8_t class[3];
180 	uint16_t romlen;
181 	uint16_t level;
182 	uint8_t arch;
183 	uint8_t indicator;
184 	uint16_t reserved_16;
185 };
186 
187 /*
188  * Callback data used while walking PCI ROM images to pick the most
189  * appropriate image.
190  */
191 
192 struct sti_pcirom_walk_ctx {
193 #ifdef STIDEBUG
194 	struct sti_softc *sc;
195 #endif
196 	bus_addr_t romoffs;
197 };
198 
199 int	sti_pcirom_check(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
200 	    const struct pcirom_ds *, void *);
201 int	sti_pcirom_walk(struct sti_softc *, bus_space_tag_t, bus_space_handle_t,
202 	    bus_size_t, int (*)(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
203 	    const struct pcirom_ds *, void *), void *);
204 
205 /*
206  * Grovel the STI ROM image.
207  */
208 
209 int
210 sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa)
211 {
212 	struct sti_softc *sc = &spc->sc_base;
213 	pcireg_t address, mask;
214 	bus_space_handle_t romh;
215 	bus_size_t romsize, stiromsize;
216 	bus_addr_t offs;
217 	uint8_t region_bars[STI_REGION_MAX];
218 	int i;
219 	int rc;
220 
221 	struct sti_pcirom_walk_ctx ctx;
222 
223 	/* sort of inline sti_pci_enable_rom(sc) */
224 	address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG);
225 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, ~PCI_ROM_ENABLE);
226 	mask = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG);
227 	address |= PCI_ROM_ENABLE;
228 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, address);
229 	sc->sc_flags |= STI_ROM_ENABLED;
230 
231 	/*
232 	 * Map the complete ROM for now.
233 	 */
234 
235 	romsize = PCI_ROM_SIZE(mask);
236 	rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address), romsize,
237 	    0, &romh);
238 	if (rc != 0) {
239 		printf("%s: can't map PCI ROM (%d)\n",
240 		    sc->sc_dev.dv_xname, rc);
241 		goto disable_return;
242 	}
243 
244 	/*
245 	 * Iterate over the ROM images, pick the best candidate.
246 	 */
247 
248 #ifdef STIDEBUG
249 	ctx.sc = sc;
250 #endif
251 	ctx.romoffs = (bus_addr_t)-1;
252 	rc = sti_pcirom_walk(sc, pa->pa_memt, romh, romsize, sti_pcirom_check,
253 	    &ctx);
254 
255 	if (ctx.romoffs == (bus_addr_t)-1) {
256 		if (rc == 0) {
257 			printf("%s: found no ROM with correct microcode"
258 			    " architecture\n", sc->sc_dev.dv_xname);
259 			rc = ENOEXEC;
260 		}
261 		goto unmap_disable_return;
262 	}
263 
264 	/*
265 	 * Read the STI region BAR assignments.
266 	 */
267 
268 	offs = ctx.romoffs +
269 	    (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, ctx.romoffs + 0x0e);
270 	bus_space_read_region_1(pa->pa_memt, romh, offs, region_bars,
271 	    STI_REGION_MAX);
272 	for (i = 0; i < STI_REGION_MAX; i++) {
273 		/*
274 		 * Region 0 is supposed to always be the ROM. FireGL-UX
275 		 * ROM agrees so well that it will report the expansion
276 		 * ROM BAR rather than any regular BAR.
277 		 * We'll address this later after remapping the ROM.
278 		 */
279 		if (i == 0 && region_bars[i] == PCI_ROM_REG)
280 			continue;
281 
282 		rc = sti_readbar(sc, pa, i, region_bars[i]);
283 		if (rc != 0)
284 			goto unmap_disable_return;
285 	}
286 
287 	/*
288 	 * Find out where the STI ROM itself lies within the PCI ROM,
289 	 * and its size.
290 	 */
291 
292 	offs = ctx.romoffs +
293 	    (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, ctx.romoffs + 0x08);
294 	stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh,
295 	    offs + 0x18);
296 	stiromsize = letoh32(stiromsize);
297 
298 	/*
299 	 * Replace our mapping with a smaller mapping of only the area
300 	 * we are interested in.
301 	 */
302 
303 	bus_space_unmap(pa->pa_memt, romh, romsize);
304 	rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address) + offs,
305 	    stiromsize, 0, &spc->sc_romh);
306 	if (rc != 0) {
307 		printf("%s: can't map STI ROM (%d)\n",
308 		    sc->sc_dev.dv_xname, rc);
309 		goto disable_return;
310 	}
311 
312 	/*
313 	 * Now set up region 0 if we had skipped it earlier.
314 	 */
315 
316 	if (region_bars[0] == PCI_ROM_REG) {
317 		sc->bases[0] =
318 		    (bus_addr_t)bus_space_vaddr(pa->pa_memt, spc->sc_romh) -
319 		    (offs - ctx.romoffs);
320 	}
321 
322 	sti_pci_disable_rom(sc);
323 	return 0;
324 
325 unmap_disable_return:
326 	bus_space_unmap(pa->pa_memt, romh, romsize);
327 disable_return:
328 	sti_pci_disable_rom(sc);
329 	return rc;
330 }
331 
332 /*
333  * Decode a BAR register.
334  */
335 int
336 sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region,
337     int bar)
338 {
339 	bus_addr_t addr;
340 	bus_size_t size;
341 	pcireg_t type;
342 	int rc;
343 
344 	if (bar == 0) {
345 		sc->bases[region] = 0;
346 		return 0;
347 	}
348 
349 	if (bar < PCI_MAPREG_START || bar > PCI_MAPREG_PPB_END) {
350 #ifdef DIAGNOSTIC
351 		printf("%s: unexpected bar %02x for region %d\n",
352 		    sc->sc_dev.dv_xname, bar, region);
353 #endif
354 		return EINVAL;
355 	}
356 
357 	type = pci_mapreg_type(pa->pa_pc, pa->pa_tag, bar);
358 	rc = pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar, type, &addr, &size,
359 	    NULL);
360 	if (rc != 0) {
361 		printf("%s: invalid bar %02x for region %d\n",
362 		    sc->sc_dev.dv_xname, bar, region);
363 		return rc;
364 	}
365 
366 	sc->bases[region] = addr;
367 	return 0;
368 }
369 
370 /*
371  * Check a PCI ROM image for an STI ROM.
372  */
373 
374 int
375 sti_pcirom_check(bus_space_tag_t romt, bus_space_handle_t romh, bus_addr_t offs,
376     const struct pcirom_ds *ds, void *v)
377 {
378 	struct sti_pcirom_walk_ctx *ctx = v;
379 #ifdef STIDEBUG
380 	struct sti_softc *sc = ctx->sc;
381 #endif
382 	uint32_t tmp32;
383 
384 	/*
385 	 * Check for a valid STI ROM header.
386 	 */
387 
388 	tmp32 = bus_space_read_4(romt, romh, offs + 0);
389 	tmp32 = letoh32(tmp32);
390 	if (tmp32 != 0x55aa0000) {
391 		/* Not an STI ROM image, onto the next */
392 #ifdef STIDEBUG
393 		printf("Invalid HP ROM signature (%08x)\n", tmp32);
394 #endif
395 		return 0;
396 	}
397 
398 	/*
399 	 * Check ROM type.
400 	 */
401 
402 	tmp32 = bus_space_read_4(romt, romh, offs + 4);
403 	tmp32 = letoh32(tmp32);
404 	if (tmp32 != 0x00000001) {	/* 1 == STI ROM */
405 #ifdef STIDEBUG
406 		printf("Unknown HP ROM type (%08x)\n", tmp32);
407 #endif
408 		return 0;
409 	}
410 
411 #ifdef STIDEBUG
412 	printf("ROM architecture code %02x", ds->arch);
413 #endif
414 	switch (ds->arch) {
415 #ifdef __hppa__
416 	/*
417 	 * The PCI specification assigns value 0x02 to PA-RISC, but
418 	 * according to the STI specification (and to hardware),
419 	 * the correct value to check for is 0x10.
420 	 */
421 	case 0x10:
422 		if (ctx->romoffs == (bus_addr_t)-1)
423 			ctx->romoffs = offs;
424 		break;
425 #endif
426 #ifdef __i386__
427 	case 0x00:
428 		if (ctx->romoffs == (bus_addr_t)-1)
429 			ctx->romoffs = offs;
430 		break;
431 #endif
432 	default:
433 #ifdef STIDEBUG
434 		printf(" (wrong architecture)");
435 #endif
436 		break;
437 	}
438 
439 #ifdef STIDEBUG
440 	if (ctx->romoffs == offs)
441 		printf(" -> SELECTED");
442 	printf("\n");
443 #endif
444 
445 	return 0;
446 }
447 
448 /*
449  * Iterate over all PCI ROM images.
450  * This code is absolutely not related to sti(4) and could be moved
451  * elsewhere in case other drivers have a need for it, but is kept
452  * here for now due to the printf() wrapper requirement (which is why
453  * there is an otherwise unnecessary softc argument).
454  */
455 
456 int
457 sti_pcirom_walk(struct sti_softc *sc, bus_space_tag_t romt,
458     bus_space_handle_t romh, bus_size_t romsize, int (*cb)(bus_space_tag_t,
459     bus_space_handle_t, bus_addr_t, const struct pcirom_ds *, void *),
460     void *cbarg)
461 {
462 	bus_addr_t offs;
463 	bus_size_t subsize;
464 	int rc = 0;
465 
466 	for (offs = 0; offs < romsize; offs += subsize) {
467 		struct pcirom_ds ds;
468 		bus_addr_t dsoffs;
469 		uint16_t tmp16;
470 
471 #ifdef STIDEBUG
472 		printf("Checking for ROM image at offset %08lx/%08lx\n",
473 		    offs, romsize);
474 #endif
475 
476 		/*
477 		 * Check for a valid ROM header (6.3.1.1).
478 		 */
479 
480 		tmp16 = bus_space_read_2(romt, romh, offs + 0);
481 		tmp16 = letoh16(tmp16);
482 		if (tmp16 != 0x55aa) {
483 			if (offs == 0) {
484 				printf("%s: invalid PCI ROM header signature"
485 				    " (%04x)\n",
486 				    sc->sc_dev.dv_xname, tmp16);
487 				rc = EINVAL;
488 			}
489 			break;
490 		}
491 
492 		/*
493 		 * Check for a valid ROM data structure (6.3.1.2).
494 		 */
495 
496 		dsoffs = (bus_addr_t)bus_space_read_2(romt, romh, offs + 0x18);
497 #ifdef STIDEBUG
498 		printf("PCI DS offset %04lx\n", dsoffs);
499 #endif
500 		if ((dsoffs & 0x03) != 0 || dsoffs < 0x1a ||
501 		    offs + dsoffs + sizeof(struct pcirom_ds) > romsize) {
502 			if (offs == 0) {
503 				printf("%s: ill-formed PCI Data Structure"
504 				    " (offset %04lx)\n",
505 				    sc->sc_dev.dv_xname, dsoffs);
506 				rc = EINVAL;
507 			} else {
508 #ifdef STIDEBUG
509 				printf("Ill-formed PCI Data Structure"
510 				    " (offset %04lx)\n", dsoffs);
511 #endif
512 			}
513 			break;
514 		}
515 
516 		bus_space_read_region_1(romt, romh, offs + dsoffs,
517 		    (uint8_t *)&ds, sizeof ds);
518 		/* convert sizes to host endianness */
519 		ds.dslen = letoh16(ds.dslen);
520 		ds.romlen = letoh16(ds.romlen);
521 #if 0 /* not used in this code */
522 		ds.vid = letoh16(ds.vid);
523 		ds.pid = letoh16(ds.pid);
524 		ds.level = letoh16(ds.level);
525 #endif
526 		if (ds.signature != 0x50434952) {	/* PCIR */
527 			if (offs == 0) {
528 				printf("%s: invalid PCI data signature"
529 				    " (%08x)\n",
530 				    sc->sc_dev.dv_xname, ds.signature);
531 				rc = EINVAL;
532 			} else {
533 #ifdef STIDEBUG
534 				printf(" invalid PCI data signature %08x\n",
535 				    ds.signature);
536 #endif
537 			}
538 			break;
539 		}
540 
541 #ifdef STIDEBUG
542 		printf("PCI DS length %04lx\n", ds.dslen);
543 #endif
544 		if (ds.dslen < sizeof ds || dsoffs + ds.dslen > romsize) {
545 			if (offs == 0) {
546 				printf("%s: ill-formed PCI Data Structure"
547 				    " (size %04lx)\n",
548 				    sc->sc_dev.dv_xname, ds.dslen);
549 				rc = EINVAL;
550 			} else {
551 #ifdef STIDEBUG
552 				printf("Ill-formed PCI Data Structure"
553 				    " (size %04lx)\n", ds.dslen);
554 #endif
555 			}
556 			break;
557 		}
558 
559 		subsize = ((bus_size_t)ds.romlen) << 9;
560 #ifdef STIDEBUG
561 		printf("ROM image size %08lx\n", subsize);
562 #endif
563 		if (subsize == 0 || offs + subsize > romsize) {
564 			if (offs == 0) {
565 				printf("%s: invalid ROM image size"
566 				    " (%04lx)\n",
567 				    sc->sc_dev.dv_xname, subsize);
568 				rc = EINVAL;
569 			} else {
570 #ifdef STIDEBUG
571 				printf("Invalid ROM image size"
572 				    " (%04lx)\n", subsize);
573 #endif
574 			}
575 			break;
576 		}
577 
578 		rc = (*cb)(romt, romh, offs, &ds, cbarg);
579 		if (rc != 0)
580 			break;
581 
582 		if ((ds.indicator & 0x80) != 0) {
583 			/* no more ROM images */
584 #ifdef STIDEBUG
585 			printf("no more ROM images\n");
586 #endif
587 			break;
588 		}
589 	}
590 
591 	return rc;
592 }
593