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