Super User's BSD Cross Reference: /OpenBSD/sys/dev/pci/sti_pci.c

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 

AltStyle によって変換されたページ (->オリジナル) /