1 /*
2 * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved.
3 * Copyright (c) 1994 Charles M. Hannum. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Charles M. Hannum.
16 * 4. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * from NetBSD: pci_machdep.c,v 1.18 2001/07/22 11:29:48 wiz Exp
31 * $FreeBSD: releng/5.0/sys/powerpc/ofw/ofw_pci.c 99661 2002-07-09 13:27:58Z benno $
32 */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/conf.h>
38 #include <sys/kernel.h>
39
40 #include <dev/ofw/openfirm.h>
41 #include <dev/ofw/ofw_pci.h>
42
43 #include <dev/pci/pcireg.h>
44 #include <dev/pci/pcivar.h>
45
46 #include <powerpc/ofw/ofw_pci.h>
47
48 #include "pcib_if.h"
49
50 static void fixup_node(device_t, phandle_t);
51 static int find_node_intr(phandle_t, u_int32_t *, u_int32_t *);
52
53 phandle_t
54 ofw_pci_find_node(device_t dev)
55 {
56 phandle_t node, nextnode;
57 u_int reg[5];
58 int l, b, s, f;
59
60 for (node = OF_peer(0); node; node = nextnode) {
61 l = OF_getprop(node, "assigned-addresses", reg, sizeof(reg));
62 if (l > 4) {
63 b = (reg[0] >> 16) & 0xff;
64 s = (reg[0] >> 11) & 0x1f;
65 f = (reg[0] >> 8) & 0x07;
66
67 if (b == pci_get_bus(dev) && s == pci_get_slot(dev) &&
68 f == pci_get_function(dev))
69 return (node);
70 }
71
72 if ((nextnode = OF_child(node)))
73 continue;
74 while (node) {
75 if ((nextnode = OF_peer(node)) != 0)
76 break;
77 node = OF_parent(node);
78 }
79 }
80
81 return (0);
82 }
83
84 void
85 ofw_pci_fixup(device_t dev, u_int bus, phandle_t parentnode)
86 {
87 phandle_t node, nextnode, childnode;
88 u_int32_t busrange[2];
89 int sz;
90
91 for (node = parentnode; node; node = nextnode) {
92 sz = OF_getprop(node, "bus-range", busrange, 8);
93 if (sz != 8 || busrange[0] != device_get_unit(dev)) {
94 for (childnode = OF_child(node); childnode;
95 childnode = OF_peer(childnode)) {
96 fixup_node(dev, childnode);
97 }
98 }
99
100 if ((nextnode = OF_child(node)) != 0)
101 continue;
102 while ((nextnode = OF_peer(node)) == 0) {
103 node = OF_parent(node);
104 if (node == parentnode)
105 return;
106 }
107 }
108 }
109
110 static void
111 fixup_node(device_t dev, phandle_t node)
112 {
113 u_int32_t csr, intr, irqs[4];
114 struct ofw_pci_register addr[8];
115 int len, i;
116
117 len = OF_getprop(node, "assigned-addresses", addr, sizeof(addr));
118 if (len < (int)sizeof(struct ofw_pci_register)) {
119 return;
120 }
121
122 csr = PCIB_READ_CONFIG(dev, OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi),
123 OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi),
124 OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_COMMAND, 4);
125 csr &= ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN);
126
127 for (i = 0; i < len / sizeof(struct ofw_pci_register); i++) {
128 switch (addr[i].phys_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
129 case OFW_PCI_PHYS_HI_SPACE_IO:
130 csr |= PCIM_CMD_PORTEN;
131 break;
132 case OFW_PCI_PHYS_HI_SPACE_MEM32:
133 csr |= PCIM_CMD_MEMEN;
134 break;
135 }
136 }
137
138 PCIB_WRITE_CONFIG(dev, OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi),
139 OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi),
140 OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_COMMAND, csr, 4);
141
142 if (find_node_intr(node, &addr[0].phys_hi, irqs) != -1) {
143 intr = PCIB_READ_CONFIG(dev,
144 OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi),
145 OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi),
146 OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_INTLINE, 2);
147 intr &= ~(0xff);
148 intr |= irqs[0] & 0xff;
149 PCIB_WRITE_CONFIG(dev,
150 OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi),
151 OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi),
152 OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_INTLINE,
153 intr, 2);
154 }
155
156 }
157
158 static int
159 find_node_intr(phandle_t node, u_int32_t *addr, u_int32_t *intr)
160 {
161 phandle_t parent, iparent;
162 int len, mlen, match, i;
163 u_int32_t map[160], *mp, imask[8], maskedaddr[8], icells;
164 char name[32];
165
166 len = OF_getprop(node, "AAPL,interrupts", intr, 4);
167 if (len == 4) {
168 return (len);
169 }
170
171 parent = OF_parent(node);
172 len = OF_getprop(parent, "interrupt-map", map, sizeof(map));
173 mlen = OF_getprop(parent, "interrupt-map-mask", imask, sizeof(imask));
174
175 if (len == -1 || mlen == -1)
176 goto nomap;
177
178 /* mask addr by "interrupt-map-mask" */
179 bcopy(addr, maskedaddr, mlen);
180 for (i = 0; i < mlen / 4; i++)
181 maskedaddr[i] &= imask[i];
182
183 mp = map;
184 while (len > mlen) {
185 match = bcmp(maskedaddr, mp, mlen);
186 mp += mlen / 4;
187 len -= mlen;
188
189 /*
190 * We must read "#interrupt-cells" for each time because
191 * interrupt-parent may be different.
192 */
193 iparent = *mp++;
194 len -= 4;
195 if (OF_getprop(iparent, "#interrupt-cells", &icells, 4) != 4)
196 goto nomap;
197
198 /* Found. */
199 if (match == 0) {
200 bcopy(mp, intr, icells * 4);
201 return (icells * 4);
202 }
203
204 mp += icells;
205 len -= icells * 4;
206 }
207
208 nomap:
209 /*
210 * If the node has no interrupt property and the parent is a PCI
211 * bridge, use the parent's interrupt. This occurs on a PCI slot.
212 */
213 bzero(name, sizeof(name));
214 OF_getprop(parent, "name", name, sizeof(name));
215 if (strcmp(name, "pci-bridge") == 0) {
216 len = OF_getprop(parent, "AAPL,interrupts", intr, 4);
217 if (len == 4) {
218 return (len);
219 }
220
221 /*
222 * XXX I don't know what is the correct local address.
223 * XXX Use the first entry for now.
224 */
225 len = OF_getprop(parent, "interrupt-map", map, sizeof(map));
226 if (len >= 36) {
227 addr = &map[5];
228 return (find_node_intr(parent, addr, intr));
229 }
230 }
231
232 /* XXX This may be wrong... */
233 len = OF_getprop(node, "interrupts", intr, 4);
234 if (len == 4) {
235 return (len);
236 }
237
238 return (-1);
239 }
Cache object: d1530c5ce32a8ecf49024bedac5eb9cc
|