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