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
pci_attach_hook(struct device * parent,struct device * self,struct pcibus_attach_args * pba)61 pci_attach_hook(struct device *parent, struct device *self,
62 struct pcibus_attach_args *pba)
63 {
64 }
65
66 int
pci_bus_maxdevs(pci_chipset_tag_t pc,int busno)67 pci_bus_maxdevs(pci_chipset_tag_t pc, int busno)
68 {
69 return (32);
70 }
71
72 pcitag_t
pci_make_tag(pci_chipset_tag_t pc,int b,int d,int f)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", ®, 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
pci_decompose_tag(pci_chipset_tag_t pc,pcitag_t tag,int * b,int * d,int * f)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
pci_conf_size(pci_chipset_tag_t pc,pcitag_t tag)133 pci_conf_size(pci_chipset_tag_t pc, pcitag_t tag)
134 {
135 return (PCI_CONFIG_SPACE_SIZE);
136 }
137
138 pcireg_t
pci_conf_read(pci_chipset_tag_t pc,pcitag_t tag,int reg)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
pci_conf_write(pci_chipset_tag_t pc,pcitag_t tag,int reg,pcireg_t data)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
pci_intr_map(struct pci_attach_args * pa,pci_intr_handle_t * ihp)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", ®, 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 *)®, 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
pci_intr_map_msi(struct pci_attach_args * pa,pci_intr_handle_t * ihp)190 pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
191 {
192 return (-1);
193 }
194
195 int
pci_intr_line(pci_chipset_tag_t pc,pci_intr_handle_t ih)196 pci_intr_line(pci_chipset_tag_t pc, pci_intr_handle_t ih)
197 {
198 return (ih);
199 }
200
201 const char *
pci_intr_string(pci_chipset_tag_t pc,pci_intr_handle_t ih)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 *
pci_intr_establish(pci_chipset_tag_t pc,pci_intr_handle_t ih,int lvl,int (* func)(void *),void * arg,const char * what)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
pci_intr_disestablish(pci_chipset_tag_t pc,void * cookie)219 pci_intr_disestablish(pci_chipset_tag_t pc, void *cookie)
220 {
221 (*intr_disestablish_func)(pc, cookie);
222 }
223
224 int
pci_ether_hw_addr(pci_chipset_tag_t pc,uint8_t * oaddr)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
ofw_enumerate_pcibus(struct pci_softc * sc,int (* match)(struct pci_attach_args *),struct pci_attach_args * pap)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", ®, 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", ®, 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
ofw_intr_map(int node,uint32_t * addr,uint32_t * intr)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