xref: /netbsd/sys/dev/pci/sti_pci.c (revision 6550d01e)
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
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
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 
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
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
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
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
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
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
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