]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/sparc64/sparc64/ofw_machdep.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / sparc64 / sparc64 / ofw_machdep.c
1 /*-
2  * Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
3  * Copyright (c) 2005 by Marius Strobl <marius@FreeBSD.org>.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26  
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 /*
31  * Some Open Firmware helper functions that are likely machine dependent.
32  */
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/systm.h>
37
38 #include <net/ethernet.h>
39
40 #include <dev/ofw/ofw_bus.h>
41 #include <dev/ofw/ofw_pci.h>
42 #include <dev/ofw/openfirm.h>
43
44 #include <machine/bus.h>
45 #include <machine/idprom.h>
46 #include <machine/ofw_bus.h>
47 #include <machine/ofw_machdep.h>
48
49 void
50 OF_getetheraddr(device_t dev, u_char *addr)
51 {
52         char buf[sizeof("true")];
53         phandle_t node;
54         struct idprom idp;
55
56         if ((node = OF_finddevice("/options")) > 0 &&
57             OF_getprop(node, "local-mac-address?", buf, sizeof(buf)) > 0) {
58                 buf[sizeof(buf) - 1] = '\0';
59                 if (strcmp(buf, "true") == 0 &&
60                     (node = ofw_bus_get_node(dev)) > 0 &&
61                     OF_getprop(node, "local-mac-address", addr,
62                     ETHER_ADDR_LEN) == ETHER_ADDR_LEN)
63                         return;
64         }
65
66         node = OF_peer(0);
67         if (node <= 0 || OF_getprop(node, "idprom", &idp, sizeof(idp)) == -1)
68                 panic("Could not determine the machine ethernet address");
69         bcopy(&idp.id_ether, addr, ETHER_ADDR_LEN);
70 }
71
72 static __inline uint32_t
73 phys_hi_mask_space(const char *bus, uint32_t phys_hi)
74 {
75         uint32_t space;
76
77         space = phys_hi;
78         if (strcmp(bus, "ebus") == 0 || strcmp(bus, "isa") == 0)
79                 space &= 0x1;
80         else if (strcmp(bus, "pci") == 0)
81                 space &= OFW_PCI_PHYS_HI_SPACEMASK;
82         /* The phys.hi cells of the other busses only contain space bits. */
83         return (space);
84 }
85
86 /*
87  * Return the physical address and the bus space to use for a node
88  * referenced by its package handle and the index of the register bank
89  * to decode. Intended to be used to together with sparc64_fake_bustag()
90  * by console drivers in early boot only.
91  * Works by mapping the address of the node's bank given in the address
92  * space of its parent upward in the device tree at each bridge along the
93  * path.
94  * Currently only really deals with max. 64-bit addresses, i.e. addresses
95  * consisting of max. 2 phys cells (phys.hi and phys.lo). If we encounter
96  * a 3 phys cells address (as with PCI addresses) we assume phys.hi can
97  * be ignored except for the space bits (generally contained in phys.hi)
98  * and treat phys.mid as phys.hi.
99  */
100 int
101 OF_decode_addr(phandle_t node, int bank, int *space, bus_addr_t *addr)
102 {
103         char name[32];
104         uint64_t cend, cstart, end, phys, sz, start;
105         pcell_t addrc, szc, paddrc;
106         phandle_t bus, lbus, pbus;
107         uint32_t banks[10 * 5]; /* 10 PCI banks */
108         uint32_t cspace, spc;
109         int i, j, nbank;
110
111         /*
112          * In general the addresses are contained in the "reg" property
113          * of a node. The first address in the "reg" property of a PCI
114          * node however is the address of its configuration registers in
115          * the configuration space of the host bridge. Additional entries
116          * denote the memory and I/O addresses. For relocatable addresses
117          * the "reg" property contains the BAR, for non-relocatable
118          * addresses it contains the absolute PCI address. The PCI-only
119          * "assigned-addresses" property however always contains the
120          * absolute PCI addresses.
121          * The "assigned-addresses" and "reg" properties are arrays of
122          * address structures consisting of #address-cells 32-bit phys
123          * cells and #size-cells 32-bit size cells. If a parent lacks
124          * the "#address-cells" or "#size-cells" property the default
125          * for #address-cells to use is 2 and for #size-cells 1.
126          */
127         bus = OF_parent(node);
128         if (bus == 0)
129                 return (ENXIO);
130         if (OF_getprop(bus, "name", name, sizeof(name)) == -1)
131                 return (ENXIO);
132         name[sizeof(name) - 1] = '\0';
133         if (OF_getprop(bus, "#address-cells", &addrc, sizeof(addrc)) == -1)
134                 addrc = 2;
135         if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1)
136                 szc = 1;
137         if (addrc < 2 || addrc > 3 || szc < 1 || szc > 2)
138                 return (ENXIO);
139         if (strcmp(name, "pci") == 0) {
140                 if (addrc > 3)
141                         return (ENXIO);
142                 nbank = OF_getprop(node, "assigned-addresses", &banks,
143                     sizeof(banks));
144         } else {
145                 if (addrc > 2)
146                         return (ENXIO);
147                 nbank = OF_getprop(node, "reg", &banks, sizeof(banks));
148         }
149         if (nbank == -1)
150                 return (ENXIO);
151         nbank /= sizeof(banks[0]) * (addrc + szc);
152         if (bank < 0 || bank > nbank - 1)
153                 return (ENXIO);
154         phys = 0;
155         for (i = 0; i < MIN(2, addrc); i++)
156                 phys |= (uint64_t)banks[(addrc + szc) * bank + addrc - 2 + i] <<
157                     32 * (MIN(2, addrc) - i - 1);
158         sz = 0;
159         for (i = 0; i < szc; i++)
160                 sz |= (uint64_t)banks[(addrc + szc) * bank + addrc + i] <<
161                     32 * (szc - i - 1);
162         start = phys;
163         end = phys + sz - 1;
164         spc = phys_hi_mask_space(name, banks[(addrc + szc) * bank]);
165
166         /*
167          * Map upward in the device tree at every bridge we encounter
168          * using their "ranges" properties.
169          * The "ranges" property of a bridge is an array of a structure
170          * consisting of that bridge's #address-cells 32-bit child-phys
171          * cells, its parent bridge #address-cells 32-bit parent-phys
172          * cells and that bridge's #size-cells 32-bit size cells.
173          * If a bridge doesn't have a "ranges" property no mapping is
174          * necessary at that bridge.
175          */
176         cspace = 0;
177         lbus = bus;
178         while ((pbus = OF_parent(bus)) != 0) {
179                 if (OF_getprop(pbus, "#address-cells", &paddrc,
180                     sizeof(paddrc)) == -1)
181                         paddrc = 2;
182                 if (paddrc < 2 || paddrc > 3)
183                         return (ENXIO);
184                 nbank = OF_getprop(bus, "ranges", &banks, sizeof(banks));
185                 if (nbank == -1) {
186                         if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
187                                 return (ENXIO);
188                         name[sizeof(name) - 1] = '\0';
189                         goto skip;
190                 }
191                 if (lbus != bus) {
192                         if (OF_getprop(bus, "#size-cells", &szc,
193                             sizeof(szc)) == -1)
194                                 szc = 1;
195                         if (szc < 1 || szc > 2)
196                                 return (ENXIO);
197                 }
198                 nbank /= sizeof(banks[0]) * (addrc + paddrc + szc);
199                 for (i = 0; i < nbank; i++) {
200                         cspace = phys_hi_mask_space(name,
201                             banks[(addrc + paddrc + szc) * i]);
202                         if (cspace != spc)
203                                 continue;
204                         phys = 0;
205                         for (j = 0; j < MIN(2, addrc); j++)
206                                 phys |= (uint64_t)banks[
207                                     (addrc + paddrc + szc) * i +
208                                     addrc - 2 + j] <<
209                                     32 * (MIN(2, addrc) - j - 1);
210                         sz = 0;
211                         for (j = 0; j < szc; j++)
212                                 sz |= (uint64_t)banks[
213                                     (addrc + paddrc + szc) * i + addrc +
214                                     paddrc + j] <<
215                                     32 * (szc - j - 1);
216                         cstart = phys;
217                         cend = phys + sz - 1;
218                         if (start < cstart || start > cend)
219                                 continue;
220                         if (end < cstart || end > cend)
221                                 return (ENXIO);
222                         phys = 0;
223                         for (j = 0; j < MIN(2, paddrc); j++)
224                                 phys |= (uint64_t)banks[
225                                     (addrc + paddrc + szc) * i + addrc +
226                                     paddrc - 2 + j] <<
227                                     32 * (MIN(2, paddrc) - j - 1);
228                         start += phys - cstart;
229                         end += phys - cstart;
230                         if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
231                                 return (ENXIO);
232                         name[sizeof(name) - 1] = '\0';
233                         spc = phys_hi_mask_space(name,
234                             banks[(addrc + paddrc + szc) * i + addrc]);
235                         break;
236                 }
237                 if (i == nbank)
238                         return (ENXIO);
239  skip:
240                 addrc = paddrc;
241                 lbus = bus;
242                 bus = pbus;
243         }
244
245         /* Done with mapping. Return the bus space as used by FreeBSD. */
246         *addr = start;
247         if (OF_parent(lbus) == 0) {
248                 *space = NEXUS_BUS_SPACE;
249                 return (0);
250         }
251         if (OF_getprop(lbus, "name", name, sizeof(name)) == -1)
252                 return (ENXIO);
253         name[sizeof(name) - 1] = '\0';
254         if (strcmp(name, "central") == 0 || strcmp(name, "upa") == 0) {
255                 *space = NEXUS_BUS_SPACE;
256                 return (0);
257         } else if (strcmp(name, "pci") == 0) {
258                 switch (cspace) {
259                 case OFW_PCI_PHYS_HI_SPACE_IO:
260                         *space = PCI_IO_BUS_SPACE;
261                         return (0);
262                 case OFW_PCI_PHYS_HI_SPACE_MEM32:
263                         *space = PCI_MEMORY_BUS_SPACE;
264                         return (0);
265                 }
266         } else if (strcmp(name, "sbus") == 0) {
267                 *space = SBUS_BUS_SPACE;
268                 return (0);
269         }
270         return (ENXIO);
271 }