xref: /netbsd-src/sys/dev/pci/sti_pci.c (revision 3014bc82c52863d3e0ed09362536510874661c98)
1 /*	$NetBSD: sti_pci.c,v 1.2 2017/10/05 06:37:45 msaitoh 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_SIZE(mr)                                                \
75             (PCI_MAPREG_ROM_ADDR(mr) & -PCI_MAPREG_ROM_ADDR(mr))
76 
77 int
sti_pci_match(device_t parent,cfdata_t cf,void * aux)78 sti_pci_match(device_t parent, cfdata_t cf, void *aux)
79 {
80 	struct pci_attach_args *paa = aux;
81 
82 	if (PCI_VENDOR(paa->pa_id) != PCI_VENDOR_HP)
83 		return 0;
84 
85 	if (PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_EG ||
86 	    PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX2 ||
87 	    PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX4 ||
88 	    PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FX6 ||
89 	    PCI_PRODUCT(paa->pa_id) == PCI_PRODUCT_HP_VISUALIZE_FXE)
90 		return 1;
91 
92 	return 0;
93 }
94 
95 void
sti_pci_attach(device_t parent,device_t self,void * aux)96 sti_pci_attach(device_t parent, device_t self, void *aux)
97 {
98 	struct sti_pci_softc *spc = device_private(self);
99 	struct pci_attach_args *paa = aux;
100 	int ret;
101 
102 	spc->sc_dev = self;
103 
104 	spc->sc_pc = paa->pa_pc;
105 	spc->sc_tag = paa->pa_tag;
106 	spc->sc_base.sc_dev = self;
107 	spc->sc_base.sc_enable_rom = sti_pci_enable_rom;
108 	spc->sc_base.sc_disable_rom = sti_pci_disable_rom;
109 
110 	aprint_normal("\n");
111 
112 	if (sti_check_rom(spc, paa) != 0)
113 		return;
114 
115 	aprint_normal("%s", device_xname(self));
116 	ret = sti_pci_is_console(paa, spc->sc_base. bases);
117 	if (ret != 0)
118 		spc->sc_base.sc_flags |= STI_CONSOLE;
119 
120 	ret = sti_attach_common(&spc->sc_base, paa->pa_iot, paa->pa_memt,
121 	    spc->sc_romh, STI_CODEBASE_MAIN);
122 	if (ret == 0)
123 		config_interrupts(self, sti_pci_end_attach);
124 
125 }
126 
sti_pci_end_attach(device_t dev)127 void sti_pci_end_attach(device_t dev)
128 {
129 	struct sti_pci_softc *spc = device_private(dev);
130 	struct sti_softc *sc = &spc->sc_base;
131 
132 	sti_end_attach(sc);
133 }
134 
135 
136 /*
137  * Grovel the STI ROM image.
138  */
139 int
sti_check_rom(struct sti_pci_softc * spc,struct pci_attach_args * pa)140 sti_check_rom(struct sti_pci_softc *spc, struct pci_attach_args *pa)
141 {
142 	struct sti_softc *sc = &spc->sc_base;
143 	pcireg_t address, mask;
144 	bus_space_handle_t romh;
145 	bus_size_t romsize, subsize, stiromsize;
146 	bus_addr_t selected, offs, suboffs;
147 	uint32_t tmp;
148 	int i;
149 	int rc;
150 
151 	/* sort of inline sti_pci_enable_rom(sc) */
152 	address = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM);
153 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM,
154 	    ~PCI_MAPREG_ROM_ENABLE);
155 	mask = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM);
156 	address |= PCI_MAPREG_ROM_ENABLE;
157 	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM, address);
158 	sc->sc_flags |= STI_ROM_ENABLED;
159 	/*
160 	 * Map the complete ROM for now.
161 	 */
162 
163 	romsize = PCI_ROM_SIZE(mask);
164 	DPRINTF(("%s: mapping rom @ %lx for %lx\n", __func__,
165 	    (long)PCI_MAPREG_ROM_ADDR(address), (long)romsize));
166 
167 	rc = bus_space_map(pa->pa_memt, PCI_MAPREG_ROM_ADDR(address), romsize,
168 	    0, &romh);
169 	if (rc != 0) {
170 		aprint_error_dev(sc->sc_dev, "can't map PCI ROM (%d)\n", rc);
171 		goto fail2;
172 	}
173 
174 	sti_pci_disable_rom_internal(spc);
175 	/*
176 	 * Iterate over the ROM images, pick the best candidate.
177 	 */
178 
179 	selected = (bus_addr_t)-1;
180 	for (offs = 0; offs < romsize; offs += subsize) {
181 		sti_pci_enable_rom_internal(spc);
182 		/*
183 		 * Check for a valid ROM header.
184 		 */
185 		tmp = bus_space_read_4(pa->pa_memt, romh, offs + 0);
186 		tmp = le32toh(tmp);
187 		if (tmp != 0x55aa0000) {
188 			sti_pci_disable_rom_internal(spc);
189 			if (offs == 0) {
190 				aprint_error_dev(sc->sc_dev,
191 				    "invalid PCI ROM header signature (%08x)\n",
192 				     tmp);
193 				rc = EINVAL;
194 			}
195 			break;
196 		}
197 
198 		/*
199 		 * Check ROM type.
200 		 */
201 		tmp = bus_space_read_4(pa->pa_memt, romh, offs + 4);
202 		tmp = le32toh(tmp);
203 		if (tmp != 0x00000001) {	/* 1 == STI ROM */
204 			sti_pci_disable_rom_internal(spc);
205 			if (offs == 0) {
206 				aprint_error_dev(sc->sc_dev,
207 				    "invalid PCI ROM type (%08x)\n", tmp);
208 				rc = EINVAL;
209 			}
210 			break;
211 		}
212 
213 		subsize = (bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
214 		    offs + 0x0c);
215 		subsize <<= 9;
216 
217 #ifdef STIDEBUG
218 		sti_pci_disable_rom_internal(spc);
219 		DPRINTF(("ROM offset %08x size %08x type %08x",
220 		    (u_int)offs, (u_int)subsize, tmp));
221 		sti_pci_enable_rom_internal(spc);
222 #endif
223 
224 		/*
225 		 * Check for a valid ROM data structure.
226 		 * We do not need it except to know what architecture the ROM
227 		 * code is for.
228 		 */
229 
230 		suboffs = offs +(bus_addr_t)bus_space_read_2(pa->pa_memt, romh,
231 		    offs + 0x18);
232 		tmp = bus_space_read_4(pa->pa_memt, romh, suboffs + 0);
233 		tmp = le32toh(tmp);
234 		if (tmp != 0x50434952) {	/* PCIR */
235 			sti_pci_disable_rom_internal(spc);
236 			if (offs == 0) {
237 				aprint_error_dev(sc->sc_dev, "invalid PCI data"
238 				    " signature (%08x)\n", tmp);
239 				rc = EINVAL;
240 			} else {
241 				DPRINTF((" invalid PCI data signature %08x\n",
242 				    tmp));
243 				continue;
244 			}
245 		}
246 
247 		tmp = bus_space_read_1(pa->pa_memt, romh, suboffs + 0x14);
248 		sti_pci_disable_rom_internal(spc);
249 		DPRINTF((" code %02x", tmp));
250 
251 		switch (tmp) {
252 #ifdef __hppa__
253 		case 0x10:
254 			if (selected == (bus_addr_t)-1)
255 				selected = offs;
256 			break;
257 #endif
258 #ifdef __i386__
259 		case 0x00:
260 			if (selected == (bus_addr_t)-1)
261 				selected = offs;
262 			break;
263 #endif
264 		default:
265 #ifdef STIDEBUG
266 			DPRINTF((" (wrong architecture)"));
267 #endif
268 			break;
269 		}
270 		DPRINTF(("%s\n", selected == offs ? " -> SELECTED" : ""));
271 	}
272 
273 	if (selected == (bus_addr_t)-1) {
274 		if (rc == 0) {
275 			aprint_error_dev(sc->sc_dev, "found no ROM with "
276 			    "correct microcode architecture\n");
277 			rc = ENOEXEC;
278 		}
279 		goto fail;
280 	}
281 
282 	/*
283 	 * Read the STI region BAR assignments.
284 	 */
285 
286 	sti_pci_enable_rom_internal(spc);
287 	offs = selected +
288 	    (bus_addr_t)bus_space_read_2(pa->pa_memt, romh, selected + 0x0e);
289 	for (i = 0; i < STI_REGION_MAX; i++) {
290 		rc = sti_readbar(sc, pa, i,
291 		    bus_space_read_1(pa->pa_memt, romh, offs + i));
292 		if (rc != 0)
293 			goto fail;
294 	}
295 
296 	/*
297 	 * Find out where the STI ROM itself lies, and its size.
298 	 */
299 
300 	offs = selected +
301 	    (bus_addr_t)bus_space_read_4(pa->pa_memt, romh, selected + 0x08);
302 	stiromsize = (bus_addr_t)bus_space_read_4(pa->pa_memt, romh,
303 	    offs + 0x18);
304 	stiromsize = le32toh(stiromsize);
305 	sti_pci_disable_rom_internal(spc);
306 
307 	/*
308 	 * Replace our mapping with a smaller mapping of only the area
309 	 * we are interested in.
310 	 */
311 
312 	DPRINTF(("remapping rom @ %lx for %lx\n",
313 	    (long)(PCI_MAPREG_ROM_ADDR(address) + offs), (long)stiromsize));
314 	bus_space_unmap(pa->pa_memt, romh, romsize);
315 	rc = bus_space_map(pa->pa_memt, PCI_MAPREG_ROM_ADDR(address) + offs,
316 	    stiromsize, 0, &spc->sc_romh);
317 	if (rc != 0) {
318 		aprint_error_dev(sc->sc_dev, "can't map STI ROM (%d)\n",
319 		    rc);
320 		goto fail2;
321 	}
322  	sti_pci_disable_rom_internal(spc);
323 	sc->sc_flags &= ~STI_ROM_ENABLED;
324 
325 	return 0;
326 
327 fail:
328 	bus_space_unmap(pa->pa_memt, romh, romsize);
329 fail2:
330 	sti_pci_disable_rom_internal(spc);
331 
332 	return rc;
333 }
334 
335 /*
336  * Decode a BAR register.
337  */
338 int
sti_readbar(struct sti_softc * sc,struct pci_attach_args * pa,u_int region,int bar)339 sti_readbar(struct sti_softc *sc, struct pci_attach_args *pa, u_int region,
340     int bar)
341 {
342 	bus_addr_t addr;
343 	bus_size_t size;
344 	uint32_t cf;
345 	int rc;
346 
347 	if (bar == 0) {
348 		sc->bases[region] = 0;
349 		return (0);
350 	}
351 
352 #ifdef DIAGNOSTIC
353 	if (bar < PCI_MAPREG_START || bar > PCI_MAPREG_PPB_END) {
354 		sti_pci_disable_rom(sc);
355 		printf("%s: unexpected bar %02x for region %d\n",
356 		    device_xname(sc->sc_dev), bar, region);
357 		sti_pci_enable_rom(sc);
358 	}
359 #endif
360 
361 	cf = pci_conf_read(pa->pa_pc, pa->pa_tag, bar);
362 
363 	rc = pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar, PCI_MAPREG_TYPE(cf),
364 	    &addr, &size, NULL);
365 
366 	if (rc != 0) {
367 		sti_pci_disable_rom(sc);
368 		aprint_error_dev(sc->sc_dev, "invalid bar %02x for region %d\n",
369 		    bar, region);
370 		sti_pci_enable_rom(sc);
371 		return (rc);
372 	}
373 
374 	sc->bases[region] = addr;
375 	return (0);
376 }
377 
378 /*
379  * Enable PCI ROM.
380  */
381 void
sti_pci_enable_rom_internal(struct sti_pci_softc * spc)382 sti_pci_enable_rom_internal(struct sti_pci_softc *spc)
383 {
384 	pcireg_t address;
385 
386 	KASSERT(spc != NULL);
387 
388 	address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM);
389 	address |= PCI_MAPREG_ROM_ENABLE;
390 	pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address);
391 }
392 
393 void
sti_pci_enable_rom(struct sti_softc * sc)394 sti_pci_enable_rom(struct sti_softc *sc)
395 {
396 	struct sti_pci_softc *spc = device_private(sc->sc_dev);
397 
398 	if (!ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
399 		sti_pci_enable_rom_internal(spc);
400 	}
401 	SET(sc->sc_flags, STI_ROM_ENABLED);
402 }
403 
404 /*
405  * Disable PCI ROM.
406  */
407 void
sti_pci_disable_rom_internal(struct sti_pci_softc * spc)408 sti_pci_disable_rom_internal(struct sti_pci_softc *spc)
409 {
410 	pcireg_t address;
411 
412 	KASSERT(spc != NULL);
413 
414 	address = pci_conf_read(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM);
415 	address &= ~PCI_MAPREG_ROM_ENABLE;
416 	pci_conf_write(spc->sc_pc, spc->sc_tag, PCI_MAPREG_ROM, address);
417 }
418 
419 void
sti_pci_disable_rom(struct sti_softc * sc)420 sti_pci_disable_rom(struct sti_softc *sc)
421 {
422 	struct sti_pci_softc *spc = device_private(sc->sc_dev);
423 
424 	if (ISSET(sc->sc_flags, STI_ROM_ENABLED)) {
425 		sti_pci_disable_rom_internal(spc);
426 	}
427 	CLR(sc->sc_flags, STI_ROM_ENABLED);
428 }
429