xref: /openbsd/sys/arch/macppc/pci/pci_machdep.c (revision d415bd75)
1 /*	$OpenBSD: pci_machdep.c,v 1.7 2016/07/04 09:30:18 mpi Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Martin Pieuchot
5  * Copyright (c) 1997 Per Fogelstrom
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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 
32 #include <machine/bus.h>
33 #include <machine/autoconf.h>
34 
35 #include <dev/pci/pcireg.h>
36 #include <dev/pci/pcivar.h>
37 
38 #include <dev/ofw/openfirm.h>
39 #include <dev/ofw/ofw_pci.h>
40 
41 
42 struct powerpc_bus_dma_tag pci_bus_dma_tag = {
43 	NULL,
44 	_dmamap_create,
45 	_dmamap_destroy,
46 	_dmamap_load,
47 	_dmamap_load_mbuf,
48 	_dmamap_load_uio,
49 	_dmamap_load_raw,
50 	_dmamap_unload,
51 	_dmamap_sync,
52 	_dmamem_alloc,
53 	_dmamem_alloc_range,
54 	_dmamem_free,
55 	_dmamem_map,
56 	_dmamem_unmap,
57 	_dmamem_mmap
58 };
59 
60 void
61 pci_attach_hook(struct device *parent, struct device *self,
62     struct pcibus_attach_args *pba)
63 {
64 }
65 
66 int
67 pci_bus_maxdevs(pci_chipset_tag_t pc, int busno)
68 {
69 	return (32);
70 }
71 
72 pcitag_t
73 pci_make_tag(pci_chipset_tag_t pc, int b, int d, int f)
74 {
75 	struct ofw_pci_register reg;
76 	pcitag_t tag;
77 	int node, busrange[2];
78 
79 	if (pc->busnode[b])
80 		return PCITAG_CREATE(0, b, d, f);
81 
82 	node = pc->pc_node;
83 
84 	/*
85 	 * Because ht(4) controller nodes do not have a "bus-range"
86 	 * property,  we need to start iterating from one of their
87 	 * PCI bridge nodes to be able to find our devices.
88 	 */
89 	if (OF_getprop(node, "bus-range", &busrange, sizeof(busrange)) < 0)
90 		node = OF_child(pc->pc_node);
91 
92 	for (; node; node = OF_peer(node)) {
93 		/*
94 		 * Check for PCI-PCI bridges.  If the device we want is
95 		 * in the bus-range for that bridge, work our way down.
96 		 */
97 		while ((OF_getprop(node, "bus-range", &busrange,
98 			sizeof(busrange)) == sizeof(busrange)) &&
99 			(b >= busrange[0] && b <= busrange[1])) {
100 			node = OF_child(node);
101 		}
102 
103 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) < sizeof(reg))
104 			continue;
105 
106 		if (b != OFW_PCI_PHYS_HI_BUS(reg.phys_hi))
107 			continue;
108 		if (d != OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi))
109 			continue;
110 		if (f != OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi))
111 			continue;
112 
113 		tag = PCITAG_CREATE(node, b, d, f);
114 
115 		return (tag);
116 	}
117 
118 	return (PCITAG_CREATE(-1, b, d, f));
119 }
120 
121 void
122 pci_decompose_tag(pci_chipset_tag_t pc, pcitag_t tag, int *b, int *d, int *f)
123 {
124 	if (b != NULL)
125 		*b = PCITAG_BUS(tag);
126 	if (d != NULL)
127 		*d = PCITAG_DEV(tag);
128 	if (f != NULL)
129 		*f = PCITAG_FUN(tag);
130 }
131 
132 int
133 pci_conf_size(pci_chipset_tag_t pc, pcitag_t tag)
134 {
135 	return (PCI_CONFIG_SPACE_SIZE);
136 }
137 
138 pcireg_t
139 pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
140 {
141         if (PCITAG_NODE(tag) != -1)
142 		return (*(pc)->pc_conf_read)(pc->pc_conf_v, tag, reg);
143 
144         return ((pcireg_t)~0);
145 }
146 
147 void
148 pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
149 {
150         if (PCITAG_NODE(tag) != -1)
151 		(*(pc)->pc_conf_write)(pc->pc_conf_v, tag, reg, data);
152 }
153 
154 int
155 pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
156 {
157 	struct ofw_pci_register reg;
158 	int node = PCITAG_NODE(pa->pa_tag);
159 	int intr[4], len;
160 
161 	if (OF_getprop(node, "reg", &reg, sizeof(reg)) < sizeof(reg))
162 		return (ENODEV);
163 
164 	/* Try to get the old Apple OFW interrupt property first. */
165 	len = OF_getprop(node, "AAPL,interrupts", &intr, sizeof(intr));
166 	if (len == sizeof(intr[0]))
167 		goto found;
168 
169 	len = OF_getprop(node, "interrupts", intr, sizeof(intr));
170 	if (len < sizeof(intr[0]))
171 		return (ENODEV);
172 
173 	reg.size_hi = intr[0];
174 	if (ofw_intr_map(OF_parent(node), (uint32_t *)&reg, intr)) {
175 		/*
176 		 * This can fail on some machines where the parent's
177 		 * node doesn't have any "interrupt-map" and friends.
178 		 *
179 		 * In this case just trust what we got in "interrupts".
180 		 */
181 	}
182 
183 found:
184 	*ihp = intr[0];
185 
186 	return (0);
187 }
188 
189 int
190 pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
191 {
192 	return (-1);
193 }
194 
195 int
196 pci_intr_line(pci_chipset_tag_t pc, pci_intr_handle_t ih)
197 {
198 	return (ih);
199 }
200 
201 const char *
202 pci_intr_string(pci_chipset_tag_t pc, pci_intr_handle_t ih)
203 {
204 	static char str[16];
205 
206 	snprintf(str, sizeof(str), "irq %ld", ih);
207 
208 	return (str);
209 }
210 
211 void *
212 pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, int lvl,
213     int (*func)(void *), void *arg, const char *what)
214 {
215 	return (*intr_establish_func)(pc, ih, IST_LEVEL, lvl, func, arg, what);
216 }
217 
218 void
219 pci_intr_disestablish(pci_chipset_tag_t pc, void *cookie)
220 {
221 	(*intr_disestablish_func)(pc, cookie);
222 }
223 
224 int
225 pci_ether_hw_addr(pci_chipset_tag_t pc, uint8_t *oaddr)
226 {
227 	uint8_t laddr[6];
228 	int node, len;
229 
230 	node = OF_finddevice("enet");
231 	len = OF_getprop(node, "local-mac-address", laddr, sizeof(laddr));
232 	if (sizeof(laddr) == len) {
233 		memcpy(oaddr, laddr, sizeof(laddr));
234 		return (1);
235 	}
236 
237 	oaddr[0] = oaddr[1] = oaddr[2] = 0xff;
238 	oaddr[3] = oaddr[4] = oaddr[5] = 0xff;
239 
240 	return (0);
241 }
242 
243 int
244 ofw_enumerate_pcibus(struct pci_softc *sc,
245     int (*match)(struct pci_attach_args *), struct pci_attach_args *pap)
246 {
247 	pci_chipset_tag_t pc = sc->sc_pc;
248 	struct ofw_pci_register reg;
249 	int len, node, b, d, f, ret;
250 	uint32_t val = 0;
251 	char compat[32];
252 	pcireg_t bhlcr;
253 	pcitag_t tag;
254 
255 	if (sc->sc_bridgetag)
256 		node = PCITAG_NODE(*sc->sc_bridgetag);
257 	else
258 		node = pc->pc_node;
259 
260 	/* The AGP bridge is not in the device-tree. */
261 	len = OF_getprop(node, "compatible", compat, sizeof(compat));
262 	if (len > 0 && strcmp(compat, "u3-agp") == 0) {
263 		tag = PCITAG_CREATE(0, sc->sc_bus, 11, 0);
264 		ret = pci_probe_device(sc, tag, match, pap);
265 		if (match != NULL && ret != 0)
266 			return (ret);
267 	}
268 
269 	/*
270 	 * An HT-PCI bridge is needed for interrupt mapping, attach it first
271 	 */
272 	if (len > 0 && strcmp(compat, "u3-ht") == 0) {
273 		int snode;
274 
275 		for (snode = OF_child(node); snode; snode = OF_peer(snode)) {
276 			val = 0;
277 			if ((OF_getprop(snode, "shasta-interrupt-sequencer",
278 			    &val, sizeof(val)) < sizeof(val)) || val != 1)
279 				continue;
280 
281 			if (OF_getprop(snode, "reg", &reg, sizeof(reg))
282 			    < sizeof(reg))
283 				continue;
284 
285 			b = OFW_PCI_PHYS_HI_BUS(reg.phys_hi);
286 			d = OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi);
287 			f = OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi);
288 
289 			tag = PCITAG_CREATE(snode, b, d, f);
290 
291 			bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
292 			if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
293 				continue;
294 
295 			ret = pci_probe_device(sc, tag, match, pap);
296 			if (match != NULL && ret != 0)
297 				return (ret);
298 		}
299 	}
300 
301 	for (node = OF_child(node); node; node = OF_peer(node)) {
302 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) < sizeof(reg))
303 			continue;
304 
305 		/*
306 		 * Skip HT-PCI bridge, it has been attached before.
307 		 */
308 		val = 0;
309 		if ((OF_getprop(node, "shasta-interrupt-sequencer", &val,
310 		    sizeof(val)) >= sizeof(val)) && val == 1)
311 			continue;
312 
313 		b = OFW_PCI_PHYS_HI_BUS(reg.phys_hi);
314 		d = OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi);
315 		f = OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi);
316 
317 		tag = PCITAG_CREATE(node, b, d, f);
318 
319 		bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
320 		if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
321 			continue;
322 
323 		ret = pci_probe_device(sc, tag, match, pap);
324 		if (match != NULL && ret != 0)
325 			return (ret);
326 	}
327 
328 	return (0);
329 }
330 
331 int
332 ofw_intr_map(int node, uint32_t *addr, uint32_t *intr)
333 {
334 	uint32_t imap[144], mmask[8], *mp, *mp1;
335 	uint32_t acells, icells, mcells;
336 	int ilen, mlen, i, step = 0;
337 	int parent;
338 
339 	ilen = OF_getprop(node, "interrupt-map", imap, sizeof(imap));
340 	mlen = OF_getprop(node, "interrupt-map-mask", mmask, sizeof(mmask));
341 	if (ilen < 0 || mlen < 0)
342 		return (-1);
343 
344 	if ((OF_getprop(node, "#address-cells", &acells, 4) < 0) ||
345 	    (OF_getprop(node, "#interrupt-cells", &icells, 4) < 0))
346 		return (-1);
347 
348 	mcells = acells + icells;
349 	if (mcells != (mlen / sizeof(mmask[0])))
350 		return (-1);
351 
352 	for (i = 0; i < mcells; i++)
353 		addr[i] &= mmask[i];
354 
355 	/* interrupt-map is formatted as follows
356 	 * int * #address-cells, int * #interrupt-cells, int, int, int
357 	 * eg
358 	 * address-cells = 3
359 	 * interrupt-cells = 1
360 	 * 00001000 00000000 00000000 00000000 ff911258 00000034 00000001
361 	 * 00001800 00000000 00000000 00000000 ff911258 00000035 00000001
362 	 * 00002000 00000000 00000000 00000000 ff911258 00000036 00000001
363 	 * | address cells          | | intr | |node| | irq  | |edge/level|
364 	 *                            | cells|        | interrupt cells   |
365 	 *                                            | of node           |
366 	 * or at least something close to that.
367 	 */
368 	for (mp = imap; ilen > mlen; mp += step) {
369 		mp1 = mp + mcells;
370 		parent = *mp1;
371 
372 		if (bcmp(mp, addr, mlen) == 0) {
373 			char ic[20];
374 
375 			/*
376 			 * If we have a match and the parent is not an
377 			 * interrupt controller continue recursively.
378 			 */
379 			if (OF_getprop(parent, "interrupt-controller", ic, 20))
380 				return ofw_intr_map(parent, &mp1[1], intr);
381 
382 			*intr = mp1[1];
383 			return (0);
384 		}
385 
386 		if (OF_getprop(parent, "#address-cells", &acells, 4) < 0)
387 			acells = 0;
388 		if (OF_getprop(parent, "#interrupt-cells", &icells, 4) < 0)
389 			break;
390 
391 		step = mcells + 1 + acells + icells;
392 		ilen -= step * sizeof(imap[0]);
393 	}
394 
395 	return (-1);
396 }
397