]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/powerpc/ofw/ofw_pci.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / powerpc / ofw / ofw_pci.c
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$
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         struct          ofw_pci_register pcir;
58         int             l, b, s, f;
59
60         for (node = OF_peer(0); node; node = nextnode) {
61                 l = OF_getprop(node, "reg", &pcir, sizeof(pcir));
62                 if (l > 4) {
63                         b = OFW_PCI_PHYS_HI_BUS(pcir.phys_hi);
64                         s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi);
65                         f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi);
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;
88
89         for (node = OF_child(parentnode); node; node = OF_peer(node)) {
90                 fixup_node(dev, node);
91         }
92 }
93
94 static void
95 fixup_node(device_t dev, phandle_t node)
96 {
97         u_int32_t       csr, intr, irqs[4], npintr, paddr[4];
98         struct          ofw_pci_register addr[8];
99         int             len, i;
100
101         len = OF_getprop(node, "assigned-addresses", addr, sizeof(addr));
102         if (len < (int)sizeof(struct ofw_pci_register)) {
103                 return;
104         }
105
106         csr = PCIB_READ_CONFIG(dev, OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi),
107             OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi),
108             OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_COMMAND, 4);
109         csr &= ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN);
110
111         for (i = 0; i < len / sizeof(struct ofw_pci_register); i++) {
112                 switch (addr[i].phys_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
113                 case OFW_PCI_PHYS_HI_SPACE_IO:
114                         csr |= PCIM_CMD_PORTEN;
115                         break;
116                 case OFW_PCI_PHYS_HI_SPACE_MEM32:
117                         csr |= PCIM_CMD_MEMEN;
118                         break;
119                 }
120         }
121
122         PCIB_WRITE_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, csr, 4);
125
126         /*
127          * Create PCI interrupt-map array element. pci-mid/pci-lo
128          * aren't required, but the 'interrupts' property needs
129          * to be appended
130          */
131         npintr = 0;
132         OF_getprop(node, "interrupts", &npintr, 4);
133         paddr[0] = addr[0].phys_hi;
134         paddr[1] = 0;
135         paddr[2] = 0;
136         paddr[3] = npintr;
137
138         if (find_node_intr(node, paddr, irqs) != -1) {
139                 intr = PCIB_READ_CONFIG(dev,
140                     OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi),
141                     OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi),
142                     OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_INTLINE, 2);
143                 intr &= ~(0xff);
144                 intr |= irqs[0] & 0xff;
145                 PCIB_WRITE_CONFIG(dev,
146                     OFW_PCI_PHYS_HI_BUS(addr[0].phys_hi),
147                     OFW_PCI_PHYS_HI_DEVICE(addr[0].phys_hi),
148                     OFW_PCI_PHYS_HI_FUNCTION(addr[0].phys_hi), PCIR_INTLINE,
149                     intr, 2);
150         }
151
152 }
153
154 static int
155 find_node_intr(phandle_t node, u_int32_t *addr, u_int32_t *intr)
156 {
157         phandle_t       parent, iparent;
158         int             len, mlen, match, i;
159         u_int32_t       map[160], *mp, imask[8], maskedaddr[8], icells;
160         char            name[32];
161
162         len = OF_getprop(node, "AAPL,interrupts", intr, 4);
163         if (len == 4) {
164                 return (len);
165         }
166
167         parent = OF_parent(node);
168         len = OF_getprop(parent, "interrupt-map", map, sizeof(map));
169         mlen = OF_getprop(parent, "interrupt-map-mask", imask, sizeof(imask));
170
171         if (len == -1 || mlen == -1)
172                 goto nomap;
173
174         memcpy(maskedaddr, addr, mlen);
175         for (i = 0; i < mlen/4; i++)
176                 maskedaddr[i] &= imask[i];
177
178         mp = map;
179         while (len > mlen) {
180                 match = bcmp(maskedaddr, mp, mlen);
181                 mp += mlen / 4;
182                 len -= mlen;
183
184                 /*
185                  * We must read "#interrupt-cells" for each time because
186                  * interrupt-parent may be different.
187                  */
188                 iparent = *mp++;
189                 len -= 4;
190                 if (OF_getprop(iparent, "#interrupt-cells", &icells, 4) != 4)
191                         goto nomap;
192
193                 /* Found. */
194                 if (match == 0) {
195                         bcopy(mp, intr, icells * 4);
196                         return (icells * 4);
197                 }
198
199                 mp += icells;
200                 len -= icells * 4;
201         }
202
203 nomap:
204         /*
205          * If the node has no interrupt property and the parent is a PCI
206          * bridge, use the parent's interrupt.  This occurs on a PCI slot.
207          */
208         bzero(name, sizeof(name));
209         OF_getprop(parent, "name", name, sizeof(name));
210         if (strcmp(name, "pci-bridge") == 0) {
211                 len = OF_getprop(parent, "AAPL,interrupts", intr, 4);
212                 if (len == 4) {
213                         return (len);
214                 }
215
216                 /*
217                  * XXX I don't know what is the correct local address.
218                  * XXX Use the first entry for now.
219                  */
220                 len = OF_getprop(parent, "interrupt-map", map, sizeof(map));
221                 if (len >= 36) {
222                         addr = &map[5];
223                         /* XXX Use 0 for 'interrupts' for compat */
224                         return (find_node_intr(parent, addr, intr));
225                 }
226         }
227
228         return (-1);
229 }