xref: /openbsd-src/sys/dev/pci/sti_pci.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /*	$OpenBSD: sti_pci.c,v 1.6 2007/06/17 12:07:10 miod Exp $	*/
2 
3 /*
4  * Copyright (c) 2006, 2007 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 
43 struct cfattach sti_pci_ca = {
44 	sizeof(struct sti_pci_softc), sti_pci_match, sti_pci_attach
45 };
46 
47 const struct pci_matchid sti_pci_devices[] = {
48 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_EG },
49 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX2 },
50 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX4 },
51 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FX6 },
52 	{ PCI_VENDOR_HP, PCI_PRODUCT_HP_VISUALIZE_FXE },
53 };
54 
55 int	sti_readbar(struct sti_softc *, struct pci_attach_args *, u_int, int);
56 int	sti_check_rom(struct sti_pci_softc *, struct pci_attach_args *);
57 void	sti_pci_enable_rom(struct sti_softc *);
58 void	sti_pci_disable_rom(struct sti_softc *);
59 
60 int	sti_pci_is_console(struct pci_attach_args *, bus_addr_t *);
61 
62 int
63 sti_pci_match(struct device *parent, void *cf, void *aux)
64 {
65 	struct pci_attach_args *paa = aux;
66 
67 	return (pci_matchbyid(paa, sti_pci_devices,
68 	    sizeof(sti_pci_devices) / sizeof(sti_pci_devices[0])));
69 }
70 
71 void
72 sti_pci_attach(struct device *parent, struct device *self, void *aux)
73 {
74 	struct sti_pci_softc *spc = (void *)self;
75 	struct pci_attach_args *paa = aux;
76 
77 	spc->sc_pc = paa->pa_pc;
78 	spc->sc_tag = paa->pa_tag;
79 	spc->sc_base.sc_enable_rom = sti_pci_enable_rom;
80 	spc->sc_base.sc_disable_rom = sti_pci_disable_rom;
81 
82 	printf("\n");
83 
84 	if (sti_check_rom(spc, paa) != 0)
85 		return;
86 
87 	printf("%s", self->dv_xname);
88 	if (sti_attach_common(&spc->sc_base, STI_CODEBASE_MAIN) == 0) {
89 		if (sti_pci_is_console(paa, spc->sc_base.bases) != 0)
90 			spc->sc_base.sc_flags |= STI_CONSOLE;
91 		startuphook_establish(sti_end_attach, spc);
92 	}
93 }
94 
95 /*
96  * Grovel the STI ROM image.
97  */
98 int
99 sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa)
100 {
101 	struct sti_softc *sc = &spc->sc_base;
102 	pcireg_t address, mask;
103 	bus_space_handle_t romh;
104 	bus_size_t romsize, subsize, stiromsize;
105 	bus_addr_t selected, offs, suboffs;
106 	u_int32_t tmp;
107 	int i;
108 	int rc;
109 
110 	/* sort of inline sti_pci_enable_rom(sc) */
111 	address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG);
112 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, ~PCI_ROM_ENABLE);
113 	mask = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG);
114 	address |= PCI_ROM_ENABLE;
115 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, address);
116 	sc->sc_flags |= STI_ROM_ENABLED;
117 
118 	/*
119 	 * Map the complete ROM for now.
120 	 */
121 
122 	romsize = PCI_ROM_SIZE(mask);
123 	rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address), romsize,
124 	    0, &romh);
125 	sti_pci_disable_rom(sc);
126 	if (rc != 0) {
127 		printf("%s: can't map PCI ROM (%d)\n",
128 		    sc->sc_dev.dv_xname, rc);
129 		goto fail2;
130 	}
131 
132 	/*
133 	 * Iterate over the ROM images, pick the best candidate.
134 	 */
135 
136 	selected = (bus_addr_t)-1;
137 	for (offs = 0; offs < romsize; offs += subsize) {
138 		sti_pci_enable_rom(sc);
139 		/*
140 		 * Check for a valid ROM header.
141 		 */
142 		tmp = bus_space_read_4(pa->pa_memt, romh, offs + 0);
143 		tmp = letoh32(tmp);
144 		if (tmp != 0x55aa0000) {
145 			sti_pci_disable_rom(sc);
146 			if (offs == 0) {
147 				printf("%s: invalid PCI ROM header signature"
148 				    " (%08x)\n",
149 				    sc->sc_dev.dv_xname, tmp);
150 				rc = EINVAL;
151 			}
152 			break;
153 		}
154 
155 		/*
156 		 * Check ROM type.
157 		 */
158 		tmp = bus_space_read_4(pa->pa_memt, romh, offs + 4);
159 		tmp = letoh32(tmp);
160 		if (tmp != 0x00000001) {	/* 1 == STI ROM */
161 			sti_pci_disable_rom(sc);
162 			if (offs == 0) {
163 				printf("%s: invalid PCI ROM type (%08x)\n",
164 				    sc->sc_dev.dv_xname, tmp);
165 				rc = EINVAL;
166 			}
167 			break;
168 		}
169 
170 		subsize = (bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
171 		    offs + 0x0c);
172 		subsize <<= 9;
173 
174 #ifdef STIDEBUG
175 		sti_pci_disable_rom(sc);
176 		printf("ROM offset %08x size %08x type %08x",
177 		    offs, subsize, tmp);
178 		sti_pci_enable_rom(sc);
179 #endif
180 
181 		/*
182 		 * Check for a valid ROM data structure.
183 		 * We do not need it except to know what architecture the ROM
184 		 * code is for.
185 		 */
186 
187 		suboffs = offs +(bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
188 		    offs + 0x18);
189 		tmp = bus_space_read_4(pa->pa_memt, romh, suboffs + 0);
190 		tmp = letoh32(tmp);
191 		if (tmp != 0x50434952) {	/* PCIR */
192 			sti_pci_disable_rom(sc);
193 			if (offs == 0) {
194 				printf("%s: invalid PCI data signature"
195 				    " (%08x)\n",
196 				    sc->sc_dev.dv_xname, tmp);
197 				rc = EINVAL;
198 			} else {
199 #ifdef STIDEBUG
200 				printf(" invalid PCI data signature %08x\n",
201 				    tmp);
202 #endif
203 				continue;
204 			}
205 		}
206 
207 		tmp = bus_space_read_1(pa->pa_memt, romh, suboffs + 0x14);
208 		sti_pci_disable_rom(sc);
209 #ifdef STIDEBUG
210 		printf(" code %02x", tmp);
211 #endif
212 
213 		switch (tmp) {
214 #ifdef __hppa__
215 		case 0x10:
216 			if (selected == (bus_addr_t)-1)
217 				selected = offs;
218 			break;
219 #endif
220 #ifdef __i386__
221 		case 0x00:
222 			if (selected == (bus_addr_t)-1)
223 				selected = offs;
224 			break;
225 #endif
226 		default:
227 #ifdef STIDEBUG
228 			printf(" (wrong architecture)");
229 #endif
230 			break;
231 		}
232 
233 #ifdef STIDEBUG
234 		if (selected == offs)
235 			printf(" -> SELECTED");
236 		printf("\n");
237 #endif
238 	}
239 
240 	if (selected == (bus_addr_t)-1) {
241 		if (rc == 0) {
242 			printf("%s: found no ROM with correct microcode"
243 			    " architecture\n", sc->sc_dev.dv_xname);
244 			rc = ENOEXEC;
245 		}
246 		goto fail;
247 	}
248 
249 	/*
250 	 * Read the STI region BAR assignments.
251 	 */
252 
253 	sti_pci_enable_rom(sc);
254 	offs = selected +
255 	    (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, selected + 0x0e);
256 	for (i = 0; i < STI_REGION_MAX; i++) {
257 		rc = sti_readbar(sc, pa, i,
258 		    bus_space_read_1(pa->pa_memt, romh, offs + i));
259 		if (rc != 0)
260 			goto fail;
261 	}
262 
263 	/*
264 	 * Find out where the STI ROM itself lies, and its size.
265 	 */
266 
267 	offs = selected +
268 	    (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, selected + 0x08);
269 	stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh,
270 	    offs + 0x18);
271 	stiromsize = letoh32(stiromsize);
272 	sti_pci_disable_rom(sc);
273 
274 	/*
275 	 * Replace our mapping with a smaller mapping of only the area
276 	 * we are interested in.
277 	 */
278 
279 	bus_space_unmap(pa->pa_memt, romh, romsize);
280 	rc = bus_space_map(pa->pa_memt, PCI_ROM_ADDR(address) + offs,
281 	    stiromsize, 0, &sc->romh);
282 	if (rc != 0) {
283 		printf("%s: can't map STI ROM (%d)\n",
284 		    sc->sc_dev.dv_xname, rc);
285 		goto fail2;
286 	}
287 	sc->memt = pa->pa_memt;
288 
289 	return (0);
290 
291 fail:
292 	bus_space_unmap(pa->pa_memt, romh, romsize);
293 fail2:
294 	sti_pci_disable_rom(sc);
295 
296 	return (rc);
297 }
298 
299 /*
300  * Decode a BAR register.
301  */
302 int
303 sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region,
304     int bar)
305 {
306 	bus_addr_t addr;
307 	bus_size_t size;
308 	u_int32_t cf;
309 	int rc;
310 
311 	if (bar == 0) {
312 		sc->bases[region] = 0;
313 		return (0);
314 	}
315 
316 #ifdef DIAGNOSTIC
317 	if (bar < PCI_MAPREG_START || bar > PCI_MAPREG_PPB_END) {
318 		sti_pci_disable_rom(sc);
319 		printf("%s: unexpected bar %02x for region %d\n",
320 		    sc->sc_dev.dv_xname, bar, region);
321 		sti_pci_enable_rom(sc);
322 	}
323 #endif
324 
325 	cf = pci_conf_read(pa->pa_pc, pa->pa_tag, bar);
326 
327 	if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO)
328 		rc = pci_io_find(pa->pa_pc, pa->pa_tag, bar, &addr, &size);
329 	else
330 		rc = pci_mem_find(pa->pa_pc, pa->pa_tag, bar, &addr, &size,
331 		    NULL);
332 
333 	if (rc != 0) {
334 		sti_pci_disable_rom(sc);
335 		printf("%s: invalid bar %02x for region %d\n",
336 		    sc->sc_dev.dv_xname, bar, region);
337 		sti_pci_enable_rom(sc);
338 		return (rc);
339 	}
340 
341 	sc->bases[region] = addr;
342 	return (0);
343 }
344 
345 /*
346  * Enable PCI ROM.
347  */
348 void
349 sti_pci_enable_rom(struct sti_softc *sc)
350 {
351 	struct sti_pci_softc *spc = (struct sti_pci_softc *)sc;
352 	pcireg_t address;
353 
354 	if (!ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
355 		address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_ROM_REG);
356 		address |= PCI_ROM_ENABLE;
357 		pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_ROM_REG, address);
358 		SET(sc->sc_flags, STI_ROM_ENABLED);
359 	}
360 }
361 
362 /*
363  * Disable PCI ROM.
364  */
365 void
366 sti_pci_disable_rom(struct sti_softc *sc)
367 {
368 	struct sti_pci_softc *spc = (struct sti_pci_softc *)sc;
369 	pcireg_t address;
370 
371 	if (ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
372 		address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_ROM_REG);
373 		address &= ~PCI_ROM_ENABLE;
374 		pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_ROM_REG, address);
375 
376 		CLR(sc->sc_flags, STI_ROM_ENABLED);
377 	}
378 }
379