1 /* $NetBSD: sti_pci.c,v 1.1 2010/11/09 12:24:48 skrll Exp $ */
2
3 /* $OpenBSD: sti_pci.c,v 1.7 2009/02/06 22:51:04 miod Exp $ */
4
5 /*
6 * Copyright (c) 2006, 2007 Miodrag Vallat.
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice, this permission notice, and the disclaimer below
11 * appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/device.h>
25
26 #include <dev/pci/pcireg.h>
27 #include <dev/pci/pcivar.h>
28 #include <dev/pci/pcidevs.h>
29
30 #include <dev/wscons/wsdisplayvar.h>
31
32 #include <dev/ic/stireg.h>
33 #include <dev/ic/stivar.h>
34
35 #ifdef STIDEBUG
36 #define DPRINTF(s) do { \
37 if (stidebug) \
38 printf s; \
39 } while(0)
40
41 extern int stidebug;
42 #else
43 #define DPRINTF(s) /* */
44 #endif
45
46 int sti_pci_match(device_t, cfdata_t, void *);
47 void sti_pci_attach(device_t, device_t, void *);
48
49 void sti_pci_end_attach(device_t dev);
50
51 struct sti_pci_softc {
52 device_t sc_dev;
53
54 struct sti_softc sc_base;
55
56 pci_chipset_tag_t sc_pc;
57 pcitag_t sc_tag;
58
59 bus_space_handle_t sc_romh;
60 };
61
62 CFATTACH_DECL_NEW(sti_pci, sizeof(struct sti_pci_softc),
63 sti_pci_match, sti_pci_attach, NULL, NULL);
64
65 int sti_readbar(struct sti_softc *, struct pci_attach_args *, u_int, int);
66 int sti_check_rom(struct sti_pci_softc *, struct pci_attach_args *);
67 void sti_pci_enable_rom(struct sti_softc *);
68 void sti_pci_disable_rom(struct sti_softc *);
69 void sti_pci_enable_rom_internal(struct sti_pci_softc *);
70 void sti_pci_disable_rom_internal(struct sti_pci_softc *);
71
72 int sti_pci_is_console(struct pci_attach_args *, bus_addr_t *);
73
74 #define PCI_ROM_ENABLE 0x00000001
75 #define PCI_ROM_ADDR_MASK 0xfffff800
76 #define PCI_ROM_ADDR(mr) \
77 ((mr) & PCI_ROM_ADDR_MASK)
78 #define PCI_ROM_SIZE(mr) \
79 (PCI_ROM_ADDR(mr) & -PCI_ROM_ADDR(mr))
80
81 int
sti_pci_match(device_t parent,cfdata_t cf,void * aux)82 sti_pci_match(device_t parent, cfdata_t cf, void *aux)
83 {
84 struct pci_attach_args *paa = aux;
85
86 if (PCI_VENDOR(paa->pa_id) != PCI_VENDOR_HP)
87 return 0;
88
89 if (PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_EG ||
90 PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX2 ||
91 PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX4 ||
92 PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX6 ||
93 PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FXE)
94 return 1;
95
96 return 0;
97 }
98
99 void
sti_pci_attach(device_t parent,device_t self,void * aux)100 sti_pci_attach(device_t parent, device_t self, void *aux)
101 {
102 struct sti_pci_softc *spc = device_private(self);
103 struct pci_attach_args *paa = aux;
104 int ret;
105
106 spc->sc_dev = self;
107
108 spc->sc_pc = paa->pa_pc;
109 spc->sc_tag = paa->pa_tag;
110 spc->sc_base.sc_dev = self;
111 spc->sc_base.sc_enable_rom = sti_pci_enable_rom;
112 spc->sc_base.sc_disable_rom = sti_pci_disable_rom;
113
114 aprint_normal("\n");
115
116 if (sti_check_rom(spc, paa) != 0)
117 return;
118
119 aprint_normal("%s", device_xname(self));
120 ret = sti_pci_is_console(paa, spc->sc_base. bases);
121 if (ret != 0)
122 spc->sc_base.sc_flags |= STI_CONSOLE;
123
124 ret = sti_attach_common(&spc->sc_base, paa->pa_iot, paa->pa_memt,
125 spc->sc_romh, STI_CODEBASE_MAIN);
126 if (ret == 0)
127 config_interrupts(self, sti_pci_end_attach);
128
129 }
130
sti_pci_end_attach(device_t dev)131 void sti_pci_end_attach(device_t dev)
132 {
133 struct sti_pci_softc *spc = device_private(dev);
134 struct sti_softc *sc = &spc->sc_base;
135
136 sti_end_attach(sc);
137 }
138
139
140 /*
141 * Grovel the STI ROM image.
142 */
143 int
sti_check_rom(struct sti_pci_softc * spc,struct pci_attach_args * pa)144 sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa)
145 {
146 struct sti_softc *sc = &spc->sc_base;
147 pcireg_t address, mask;
148 bus_space_handle_t romh;
149 bus_size_t romsize, subsize, stiromsize;
150 bus_addr_t selected, offs, suboffs;
151 uint32_t tmp;
152 int i;
153 int rc;
154
155 /* sort of inline sti_pci_enable_rom(sc) */
156 address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM);
157 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM, ~PCI_ROM_ENABLE);
158 mask = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM);
159 address |= PCI_ROM_ENABLE;
160 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM, address);
161 sc->sc_flags |= STI_ROM_ENABLED;
162 /*
163 * Map the complete ROM for now.
164 */
165
166 romsize = PCI_ROM_SIZE(mask);
167 DPRINTF(("%s: mapping rom @ %lx for %lx\n", __func__,
168 (long)PCI_ROM_ADDR(address), (long)romsize));
169
170 rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address), romsize,
171 0, &romh);
172 if (rc != 0) {
173 aprint_error_dev(sc->sc_dev, "can't map PCI ROM (%d)\n", rc);
174 goto fail2;
175 }
176
177 sti_pci_disable_rom_internal(spc);
178 /*
179 * Iterate over the ROM images, pick the best candidate.
180 */
181
182 selected = (bus_addr_t)-1;
183 for (offs = 0; offs < romsize; offs += subsize) {
184 sti_pci_enable_rom_internal(spc);
185 /*
186 * Check for a valid ROM header.
187 */
188 tmp = bus_space_read_4(pa->pa_memt, romh, offs + 0);
189 tmp = le32toh(tmp);
190 if (tmp != 0x55aa0000) {
191 sti_pci_disable_rom_internal(spc);
192 if (offs == 0) {
193 aprint_error_dev(sc->sc_dev,
194 "invalid PCI ROM header signature (%08x)\n",
195 tmp);
196 rc = EINVAL;
197 }
198 break;
199 }
200
201 /*
202 * Check ROM type.
203 */
204 tmp = bus_space_read_4(pa->pa_memt, romh, offs + 4);
205 tmp = le32toh(tmp);
206 if (tmp != 0x00000001) { /* 1 == STI ROM */
207 sti_pci_disable_rom_internal(spc);
208 if (offs == 0) {
209 aprint_error_dev(sc->sc_dev,
210 "invalid PCI ROM type (%08x)\n", tmp);
211 rc = EINVAL;
212 }
213 break;
214 }
215
216 subsize = (bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
217 offs + 0x0c);
218 subsize <<= 9;
219
220 #ifdef STIDEBUG
221 sti_pci_disable_rom_internal(spc);
222 DPRINTF(("ROM offset %08x size %08x type %08x",
223 (u_int)offs, (u_int)subsize, tmp));
224 sti_pci_enable_rom_internal(spc);
225 #endif
226
227 /*
228 * Check for a valid ROM data structure.
229 * We do not need it except to know what architecture the ROM
230 * code is for.
231 */
232
233 suboffs = offs +(bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
234 offs + 0x18);
235 tmp = bus_space_read_4(pa->pa_memt, romh, suboffs + 0);
236 tmp = le32toh(tmp);
237 if (tmp != 0x50434952) { /* PCIR */
238 sti_pci_disable_rom_internal(spc);
239 if (offs == 0) {
240 aprint_error_dev(sc->sc_dev, "invalid PCI data"
241 " signature (%08x)\n", tmp);
242 rc = EINVAL;
243 } else {
244 DPRINTF((" invalid PCI data signature %08x\n",
245 tmp));
246 continue;
247 }
248 }
249
250 tmp = bus_space_read_1(pa->pa_memt, romh, suboffs + 0x14);
251 sti_pci_disable_rom_internal(spc);
252 DPRINTF((" code %02x", tmp));
253
254 switch (tmp) {
255 #ifdef __hppa__
256 case 0x10:
257 if (selected == (bus_addr_t)-1)
258 selected = offs;
259 break;
260 #endif
261 #ifdef __i386__
262 case 0x00:
263 if (selected == (bus_addr_t)-1)
264 selected = offs;
265 break;
266 #endif
267 default:
268 #ifdef STIDEBUG
269 DPRINTF((" (wrong architecture)"));
270 #endif
271 break;
272 }
273 DPRINTF(("%s\n", selected == offs ? " -> SELECTED" : ""));
274 }
275
276 if (selected == (bus_addr_t)-1) {
277 if (rc == 0) {
278 aprint_error_dev(sc->sc_dev, "found no ROM with "
279 "correct microcode architecture\n");
280 rc = ENOEXEC;
281 }
282 goto fail;
283 }
284
285 /*
286 * Read the STI region BAR assignments.
287 */
288
289 sti_pci_enable_rom_internal(spc);
290 offs = selected +
291 (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, selected + 0x0e);
292 for (i = 0; i < STI_REGION_MAX; i++) {
293 rc = sti_readbar(sc, pa, i,
294 bus_space_read_1(pa->pa_memt, romh, offs + i));
295 if (rc != 0)
296 goto fail;
297 }
298
299 /*
300 * Find out where the STI ROM itself lies, and its size.
301 */
302
303 offs = selected +
304 (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, selected + 0x08);
305 stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh,
306 offs + 0x18);
307 stiromsize = le32toh(stiromsize);
308 sti_pci_disable_rom_internal(spc);
309
310 /*
311 * Replace our mapping with a smaller mapping of only the area
312 * we are interested in.
313 */
314
315 DPRINTF(("remapping rom @ %lx for %lx\n",
316 (long)(PCI_ROM_ADDR(address) + offs), (long)stiromsize));
317 bus_space_unmap(pa->pa_memt, romh, romsize);
318 rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address) + offs,
319 stiromsize, 0, &spc->sc_romh);
320 if (rc != 0) {
321 aprint_error_dev(sc->sc_dev, "can't map STI ROM (%d)\n",
322 rc);
323 goto fail2;
324 }
325 sti_pci_disable_rom_internal(spc);
326 sc->sc_flags &= ~STI_ROM_ENABLED;
327
328 return 0;
329
330 fail:
331 bus_space_unmap(pa->pa_memt, romh, romsize);
332 fail2:
333 sti_pci_disable_rom_internal(spc);
334
335 return rc;
336 }
337
338 /*
339 * Decode a BAR register.
340 */
341 int
sti_readbar(struct sti_softc * sc,struct pci_attach_args * pa,u_int region,int bar)342 sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region,
343 int bar)
344 {
345 bus_addr_t addr;
346 bus_size_t size;
347 uint32_t cf;
348 int rc;
349
350 if (bar == 0) {
351 sc->bases[region] = 0;
352 return (0);
353 }
354
355 #ifdef DIAGNOSTIC
356 if (bar < PCI_MAPREG_START || bar > PCI_MAPREG_PPB_END) {
357 sti_pci_disable_rom(sc);
358 printf("%s: unexpected bar %02x for region %d\n",
359 device_xname(sc->sc_dev), bar, region);
360 sti_pci_enable_rom(sc);
361 }
362 #endif
363
364 cf = pci_conf_read(pa->pa_pc, pa->pa_tag, bar);
365
366 rc = pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar, PCI_MAPREG_TYPE(cf),
367 &addr, &size, NULL);
368
369 if (rc != 0) {
370 sti_pci_disable_rom(sc);
371 aprint_error_dev(sc->sc_dev, "invalid bar %02x for region %d\n",
372 bar, region);
373 sti_pci_enable_rom(sc);
374 return (rc);
375 }
376
377 sc->bases[region] = addr;
378 return (0);
379 }
380
381 /*
382 * Enable PCI ROM.
383 */
384 void
sti_pci_enable_rom_internal(struct sti_pci_softc * spc)385 sti_pci_enable_rom_internal(struct sti_pci_softc *spc)
386 {
387 pcireg_t address;
388
389 KASSERT(spc != NULL);
390
391 address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM);
392 address |= PCI_ROM_ENABLE;
393 pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address);
394 }
395
396 void
sti_pci_enable_rom(struct sti_softc * sc)397 sti_pci_enable_rom(struct sti_softc *sc)
398 {
399 struct sti_pci_softc *spc = device_private(sc->sc_dev);
400
401 if (!ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
402 sti_pci_enable_rom_internal(spc);
403 }
404 SET(sc->sc_flags, STI_ROM_ENABLED);
405 }
406
407 /*
408 * Disable PCI ROM.
409 */
410 void
sti_pci_disable_rom_internal(struct sti_pci_softc * spc)411 sti_pci_disable_rom_internal(struct sti_pci_softc *spc)
412 {
413 pcireg_t address;
414
415 KASSERT(spc != NULL);
416
417 address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM);
418 address &= ~PCI_ROM_ENABLE;
419 pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address);
420 }
421
422 void
sti_pci_disable_rom(struct sti_softc * sc)423 sti_pci_disable_rom(struct sti_softc *sc)
424 {
425 struct sti_pci_softc *spc = device_private(sc->sc_dev);
426
427 if (ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
428 sti_pci_disable_rom_internal(spc);
429 }
430 CLR(sc->sc_flags, STI_ROM_ENABLED);
431 }
432