xref: /netbsd-src/sys/arch/macppc/pci/pci_machdep.c (revision eb7c1594f145c931049e1fd9eb056a5987e87e59)
1 /*	$NetBSD: pci_machdep.c,v 1.21 2003/07/15 02:43:33 lukem Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 Christopher G. Demetriou.  All rights reserved.
5  * Copyright (c) 1994 Charles M. Hannum.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Charles M. Hannum.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Machine-specific functions for PCI autoconfiguration.
35  *
36  * On PCs, there are two methods of generating PCI configuration cycles.
37  * We try to detect the appropriate mechanism for this machine and set
38  * up a few function pointers to access the correct method directly.
39  *
40  * The configuration method can be hard-coded in the config file by
41  * using `options PCI_CONF_MODE=N', where `N' is the configuration mode
42  * as defined section 3.6.4.1, `Generating Configuration Cycles'.
43  */
44 
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: pci_machdep.c,v 1.21 2003/07/15 02:43:33 lukem Exp $");
47 
48 #include <sys/types.h>
49 #include <sys/param.h>
50 #include <sys/time.h>
51 #include <sys/systm.h>
52 #include <sys/errno.h>
53 #include <sys/device.h>
54 
55 #include <uvm/uvm_extern.h>
56 
57 #define _MACPPC_BUS_DMA_PRIVATE
58 #include <machine/bus.h>
59 
60 #include <machine/bus.h>
61 #include <machine/pio.h>
62 #include <machine/intr.h>
63 
64 #include <dev/pci/pcivar.h>
65 #include <dev/pci/pcireg.h>
66 
67 #include <dev/ofw/openfirm.h>
68 #include <dev/ofw/ofw_pci.h>
69 
70 static void fixpci __P((int, pci_chipset_tag_t));
71 static int find_node_intr __P((int, u_int32_t *, u_int32_t *));
72 
73 /*
74  * PCI doesn't have any special needs; just use the generic versions
75  * of these functions.
76  */
77 struct macppc_bus_dma_tag pci_bus_dma_tag = {
78 	0,			/* _bounce_thresh */
79 	_bus_dmamap_create,
80 	_bus_dmamap_destroy,
81 	_bus_dmamap_load,
82 	_bus_dmamap_load_mbuf,
83 	_bus_dmamap_load_uio,
84 	_bus_dmamap_load_raw,
85 	_bus_dmamap_unload,
86 	NULL,			/* _dmamap_sync */
87 	_bus_dmamem_alloc,
88 	_bus_dmamem_free,
89 	_bus_dmamem_map,
90 	_bus_dmamem_unmap,
91 	_bus_dmamem_mmap,
92 };
93 
94 void
95 pci_attach_hook(parent, self, pba)
96 	struct device *parent, *self;
97 	struct pcibus_attach_args *pba;
98 {
99 	pci_chipset_tag_t pc = pba->pba_pc;
100 	int bus = pba->pba_bus;
101 	int node, nn, sz;
102 	int32_t busrange[2];
103 
104 	for (node = pc->node; node; node = nn) {
105 		sz = OF_getprop(node, "bus-range", busrange, 8);
106 		if (sz == 8 && busrange[0] == bus) {
107 			fixpci(node, pc);
108 			return;
109 		}
110 		if ((nn = OF_child(node)) != 0)
111 			continue;
112 		while ((nn = OF_peer(node)) == 0) {
113 			node = OF_parent(node);
114 			if (node == pc->node)
115 				return;		/* not found */
116 		}
117 	}
118 }
119 
120 int
121 pci_bus_maxdevs(pc, busno)
122 	pci_chipset_tag_t pc;
123 	int busno;
124 {
125 
126 	/*
127 	 * Bus number is irrelevant.  Configuration Mechanism 1 is in
128 	 * use, can have devices 0-32 (i.e. the `normal' range).
129 	 */
130 	return 32;
131 }
132 
133 pcitag_t
134 pci_make_tag(pc, bus, device, function)
135 	pci_chipset_tag_t pc;
136 	int bus, device, function;
137 {
138 	pcitag_t tag;
139 
140 	if (bus >= 256 || device >= 32 || function >= 8)
141 		panic("pci_make_tag: bad request");
142 
143 	/* XXX magic number */
144 	tag = 0x80000000 | (bus << 16) | (device << 11) | (function << 8);
145 
146 	return tag;
147 }
148 
149 void
150 pci_decompose_tag(pc, tag, bp, dp, fp)
151 	pci_chipset_tag_t pc;
152 	pcitag_t tag;
153 	int *bp, *dp, *fp;
154 {
155 
156 	if (bp != NULL)
157 		*bp = (tag >> 16) & 0xff;
158 	if (dp != NULL)
159 		*dp = (tag >> 11) & 0x1f;
160 	if (fp != NULL)
161 		*fp = (tag >> 8) & 0x07;
162 }
163 
164 pcireg_t
165 pci_conf_read(pc, tag, reg)
166 	pci_chipset_tag_t pc;
167 	pcitag_t tag;
168 	int reg;
169 {
170 
171 	return (*pc->conf_read)(pc, tag, reg);
172 }
173 
174 void
175 pci_conf_write(pc, tag, reg, data)
176 	pci_chipset_tag_t pc;
177 	pcitag_t tag;
178 	int reg;
179 	pcireg_t data;
180 {
181 
182 	(*pc->conf_write)(pc, tag, reg, data);
183 }
184 
185 int
186 pci_intr_map(pa, ihp)
187 	struct pci_attach_args *pa;
188 	pci_intr_handle_t *ihp;
189 {
190 	int pin = pa->pa_intrpin;
191 	int line = pa->pa_intrline;
192 
193 	if (pin == 0) {
194 		/* No IRQ used. */
195 		goto bad;
196 	}
197 
198 	if (pin > 4) {
199 		printf("pci_intr_map: bad interrupt pin %d\n", pin);
200 		goto bad;
201 	}
202 
203 	/*
204 	 * Section 6.2.4, `Miscellaneous Functions', says that 255 means
205 	 * `unknown' or `no connection' on a PC.  We assume that a device with
206 	 * `no connection' either doesn't have an interrupt (in which case the
207 	 * pin number should be 0, and would have been noticed above), or
208 	 * wasn't configured by the BIOS (in which case we punt, since there's
209 	 * no real way we can know how the interrupt lines are mapped in the
210 	 * hardware).
211 	 *
212 	 * XXX
213 	 * Since IRQ 0 is only used by the clock, and we can't actually be sure
214 	 * that the BIOS did its job, we also recognize that as meaning that
215 	 * the BIOS has not configured the device.
216 	 */
217 	if (line == 0 || line == 255) {
218 		printf("pci_intr_map: no mapping for pin %c\n", '@' + pin);
219 		goto bad;
220 	} else {
221 		if (line >= ICU_LEN) {
222 			printf("pci_intr_map: bad interrupt line %d\n", line);
223 			goto bad;
224 		}
225 	}
226 
227 	*ihp = line;
228 	return 0;
229 
230 bad:
231 	*ihp = -1;
232 	return 1;
233 }
234 
235 const char *
236 pci_intr_string(pc, ih)
237 	pci_chipset_tag_t pc;
238 	pci_intr_handle_t ih;
239 {
240 	static char irqstr[8];		/* 4 + 2 + NULL + sanity */
241 
242 	if (ih == 0 || ih >= ICU_LEN)
243 		panic("pci_intr_string: bogus handle 0x%x", ih);
244 
245 	sprintf(irqstr, "irq %d", ih);
246 	return (irqstr);
247 
248 }
249 
250 const struct evcnt *
251 pci_intr_evcnt(pc, ih)
252 	pci_chipset_tag_t pc;
253 	pci_intr_handle_t ih;
254 {
255 
256 	/* XXX for now, no evcnt parent reported */
257 	return NULL;
258 }
259 
260 void *
261 pci_intr_establish(pc, ih, level, func, arg)
262 	pci_chipset_tag_t pc;
263 	pci_intr_handle_t ih;
264 	int level, (*func) __P((void *));
265 	void *arg;
266 {
267 
268 	if (ih == 0 || ih >= ICU_LEN)
269 		panic("pci_intr_establish: bogus handle 0x%x", ih);
270 
271 	return intr_establish(ih, IST_LEVEL, level, func, arg);
272 }
273 
274 void
275 pci_intr_disestablish(pc, cookie)
276 	pci_chipset_tag_t pc;
277 	void *cookie;
278 {
279 
280 	intr_disestablish(cookie);
281 }
282 
283 #define pcibus(x) \
284 	(((x) & OFW_PCI_PHYS_HI_BUSMASK) >> OFW_PCI_PHYS_HI_BUSSHIFT)
285 #define pcidev(x) \
286 	(((x) & OFW_PCI_PHYS_HI_DEVICEMASK) >> OFW_PCI_PHYS_HI_DEVICESHIFT)
287 #define pcifunc(x) \
288 	(((x) & OFW_PCI_PHYS_HI_FUNCTIONMASK) >> OFW_PCI_PHYS_HI_FUNCTIONSHIFT)
289 
290 void
291 fixpci(parent, pc)
292 	int parent;
293 	pci_chipset_tag_t pc;
294 {
295 	int node;
296 	pcitag_t tag;
297 	pcireg_t csr, intr;
298 	int len, i;
299 	int32_t irqs[4];
300 	struct {
301 		u_int32_t phys_hi, phys_mid, phys_lo;
302 		u_int32_t size_hi, size_lo;
303 	} addr[8];
304 
305 	for (node = OF_child(parent); node; node = OF_peer(node)) {
306 		len = OF_getprop(node, "assigned-addresses", addr,
307 				 sizeof(addr));
308 		if (len < (int)sizeof(addr[0]))
309 			continue;
310 
311 		tag = pci_make_tag(pc, pcibus(addr[0].phys_hi),
312 				   pcidev(addr[0].phys_hi),
313 				   pcifunc(addr[0].phys_hi));
314 
315 		/*
316 		 * Make sure the IO and MEM enable bits are set in the CSR.
317 		 */
318 		csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
319 		csr &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);
320 
321 		for (i = 0; i < len / sizeof(addr[0]); i++) {
322 			switch (addr[i].phys_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
323 			case OFW_PCI_PHYS_HI_SPACE_IO:
324 				csr |= PCI_COMMAND_IO_ENABLE;
325 				break;
326 
327 			case OFW_PCI_PHYS_HI_SPACE_MEM32:
328 			case OFW_PCI_PHYS_HI_SPACE_MEM64:
329 				csr |= PCI_COMMAND_MEM_ENABLE;
330 				break;
331 			}
332 		}
333 
334 		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);
335 
336 		/*
337 		 * Make sure the line register is programmed with the
338 		 * interrupt mapping.
339 		 */
340 		if (find_node_intr(node, &addr[0].phys_hi, irqs) == -1)
341 			continue;
342 
343 		intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
344 		intr &= ~PCI_INTERRUPT_LINE_MASK;
345 		intr |= irqs[0] & PCI_INTERRUPT_LINE_MASK;
346 		pci_conf_write(pc, tag, PCI_INTERRUPT_REG, intr);
347 	}
348 }
349 
350 /*
351  * Find PCI IRQ of the node from OF tree.
352  */
353 int
354 find_node_intr(node, addr, intr)
355 	int node;
356 	u_int32_t *addr, *intr;
357 {
358 	int parent, len, mlen, iparent;
359 	int match, i;
360 	u_int32_t map[160], *mp;
361 	u_int32_t imask[8], maskedaddr[8];
362 	u_int32_t icells;
363 	char name[32];
364 
365 	len = OF_getprop(node, "AAPL,interrupts", intr, 4) ;
366 	if (len == 4)
367 		return len;
368 
369 	parent = OF_parent(node);
370 	len = OF_getprop(parent, "interrupt-map", map, sizeof(map));
371 	mlen = OF_getprop(parent, "interrupt-map-mask", imask, sizeof(imask));
372 
373 	if (len == -1 || mlen == -1)
374 		goto nomap;
375 
376 #ifdef DIAGNOSTIC
377 	if (mlen == sizeof(imask)) {
378 		printf("interrupt-map too long\n");
379 		return -1;
380 	}
381 #endif
382 
383 	/* mask addr by "interrupt-map-mask" */
384 	memcpy(maskedaddr, addr, mlen);
385 	for (i = 0; i < mlen / 4; i++)
386 		maskedaddr[i] &= imask[i];
387 
388 	mp = map;
389 	while (len > mlen) {
390 		match = memcmp(maskedaddr, mp, mlen);
391 		mp += mlen / 4;
392 		len -= mlen;
393 
394 		/*
395 		 * We must read "#interrupt-cells" for each time because
396 		 * interrupt-parent may be different.
397 		 */
398 		iparent = *mp++;
399 		len -= 4;
400 		if (OF_getprop(iparent, "#interrupt-cells", &icells, 4) != 4)
401 			goto nomap;
402 
403 		/* Found. */
404 		if (match == 0) {
405 			memcpy(intr, mp, icells * 4);
406 			return icells * 4;
407 		}
408 
409 		mp += icells;
410 		len -= icells * 4;
411 	}
412 
413 nomap:
414 	/*
415 	 * If the node has no interrupt property and the parent is a
416 	 * pci-bridge, use parent's interrupt.  This occurs on a PCI
417 	 * slot.  (e.g. AHA-3940)
418 	 */
419 	memset(name, 0, sizeof(name));
420 	OF_getprop(parent, "name", name, sizeof(name));
421 	if (strcmp(name, "pci-bridge") == 0) {
422 		len = OF_getprop(parent, "AAPL,interrupts", intr, 4) ;
423 		if (len == 4)
424 			return len;
425 		/*
426 		 * XXX I don't know what is the correct local address.
427 		 * XXX Use the first entry for now.
428 		 */
429 		len = OF_getprop(parent, "interrupt-map", map, sizeof(map));
430 		if (len >= 36) {
431 			addr = &map[5];
432 			return find_node_intr(parent, addr, intr);
433 		}
434 	}
435 
436 	/* XXX This may be wrong... */
437 	len = OF_getprop(node, "interrupts", intr, 4) ;
438 	if (len == 4)
439 		return len;
440 
441 	return -1;
442 }
443