| @@ -1,543 +1,532 @@ | | | @@ -1,543 +1,532 @@ |
1 | /* $NetBSD: fdt_subr.c,v 1.38 2020/07/16 16:39:18 jmcneill Exp $ */ | | 1 | /* $NetBSD: fdt_subr.c,v 1.39 2021/01/24 15:43:22 thorpej Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> | | 4 | * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * | | 15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | 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 | | 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | | 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | | 21 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | | 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | | 23 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
24 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 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 | | 25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 | * SUCH DAMAGE. | | 26 | * SUCH DAMAGE. |
27 | */ | | 27 | */ |
28 | | | 28 | |
29 | #include <sys/cdefs.h> | | 29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: fdt_subr.c,v 1.38 2020/07/16 16:39:18 jmcneill Exp $"); | | 30 | __KERNEL_RCSID(0, "$NetBSD: fdt_subr.c,v 1.39 2021/01/24 15:43:22 thorpej Exp $"); |
31 | | | 31 | |
32 | #include "opt_fdt.h" | | 32 | #include "opt_fdt.h" |
33 | | | 33 | |
34 | #include <sys/param.h> | | 34 | #include <sys/param.h> |
35 | #include <sys/bus.h> | | 35 | #include <sys/bus.h> |
36 | | | 36 | |
37 | #include <libfdt.h> | | 37 | #include <libfdt.h> |
38 | #include <dev/fdt/fdtvar.h> | | 38 | #include <dev/fdt/fdtvar.h> |
39 | #include <dev/fdt/fdt_private.h> | | 39 | #include <dev/fdt/fdt_private.h> |
40 | | | 40 | |
41 | #ifndef FDT_DEFAULT_STDOUT_PATH | | 41 | #ifndef FDT_DEFAULT_STDOUT_PATH |
42 | #define FDT_DEFAULT_STDOUT_PATH "serial0:115200n8" | | 42 | #define FDT_DEFAULT_STDOUT_PATH "serial0:115200n8" |
43 | #endif | | 43 | #endif |
44 | | | 44 | |
45 | static const void *fdt_data; | | 45 | static const void *fdt_data; |
46 | | | 46 | |
47 | static struct fdt_conslist fdt_console_list = | | 47 | static struct fdt_conslist fdt_console_list = |
48 | TAILQ_HEAD_INITIALIZER(fdt_console_list); | | 48 | TAILQ_HEAD_INITIALIZER(fdt_console_list); |
49 | | | 49 | |
50 | bool | | 50 | bool |
51 | fdtbus_init(const void *data) | | 51 | fdtbus_init(const void *data) |
52 | { | | 52 | { |
53 | KASSERT(fdt_data == NULL); | | 53 | KASSERT(fdt_data == NULL); |
54 | if (fdt_check_header(data) != 0) { | | 54 | if (fdt_check_header(data) != 0) { |
55 | return false; | | 55 | return false; |
56 | } | | 56 | } |
57 | fdt_data = data; | | 57 | fdt_data = data; |
58 | | | 58 | |
59 | return true; | | 59 | return true; |
60 | } | | 60 | } |
61 | | | 61 | |
62 | const void * | | 62 | const void * |
63 | fdtbus_get_data(void) | | 63 | fdtbus_get_data(void) |
64 | { | | 64 | { |
65 | return fdt_data; | | 65 | return fdt_data; |
66 | } | | 66 | } |
67 | | | 67 | |
68 | int | | 68 | int |
69 | fdtbus_offset2phandle(int offset) | | 69 | fdtbus_offset2phandle(int offset) |
70 | { | | 70 | { |
71 | if (offset < 0) | | 71 | if (offset < 0) |
72 | return 0; | | 72 | return 0; |
73 | | | 73 | |
74 | return offset + fdt_off_dt_struct(fdt_data); | | 74 | return offset + fdt_off_dt_struct(fdt_data); |
75 | } | | 75 | } |
76 | | | 76 | |
77 | int | | 77 | int |
78 | fdtbus_phandle2offset(int phandle) | | 78 | fdtbus_phandle2offset(int phandle) |
79 | { | | 79 | { |
80 | const int dtoff = fdt_off_dt_struct(fdt_data); | | 80 | const int dtoff = fdt_off_dt_struct(fdt_data); |
81 | | | 81 | |
82 | if (phandle == -1) | | 82 | if (phandle == -1) |
83 | phandle = dtoff; | | 83 | phandle = dtoff; |
84 | | | 84 | |
85 | if (phandle < dtoff) | | 85 | if (phandle < dtoff) |
86 | return -1; | | 86 | return -1; |
87 | | | 87 | |
88 | return phandle - dtoff; | | 88 | return phandle - dtoff; |
89 | } | | 89 | } |
90 | | | 90 | |
91 | static bool fdtbus_decoderegprop = true; | | 91 | static bool fdtbus_decoderegprop = true; |
92 | | | 92 | |
93 | void | | 93 | void |
94 | fdtbus_set_decoderegprop(bool decode) | | 94 | fdtbus_set_decoderegprop(bool decode) |
95 | { | | 95 | { |
96 | fdtbus_decoderegprop = decode; | | 96 | fdtbus_decoderegprop = decode; |
97 | } | | 97 | } |
98 | | | 98 | |
99 | int | | 99 | int |
100 | fdtbus_get_addr_cells(int phandle) | | 100 | fdtbus_get_addr_cells(int phandle) |
101 | { | | 101 | { |
102 | uint32_t addr_cells; | | 102 | uint32_t addr_cells; |
103 | | | 103 | |
104 | if (of_getprop_uint32(phandle, "#address-cells", &addr_cells)) | | 104 | if (of_getprop_uint32(phandle, "#address-cells", &addr_cells)) |
105 | addr_cells = 2; | | 105 | addr_cells = 2; |
106 | | | 106 | |
107 | return addr_cells; | | 107 | return addr_cells; |
108 | } | | 108 | } |
109 | | | 109 | |
110 | int | | 110 | int |
111 | fdtbus_get_size_cells(int phandle) | | 111 | fdtbus_get_size_cells(int phandle) |
112 | { | | 112 | { |
113 | uint32_t size_cells; | | 113 | uint32_t size_cells; |
114 | | | 114 | |
115 | if (of_getprop_uint32(phandle, "#size-cells", &size_cells)) | | 115 | if (of_getprop_uint32(phandle, "#size-cells", &size_cells)) |
116 | size_cells = 0; | | 116 | size_cells = 0; |
117 | | | 117 | |
118 | return size_cells; | | 118 | return size_cells; |
119 | } | | 119 | } |
120 | | | 120 | |
121 | int | | 121 | int |
122 | fdtbus_get_phandle(int phandle, const char *prop) | | 122 | fdtbus_get_phandle(int phandle, const char *prop) |
123 | { | | 123 | { |
124 | u_int phandle_ref; | | 124 | u_int phandle_ref; |
125 | const u_int *buf; | | 125 | const u_int *buf; |
126 | int len; | | 126 | int len; |
127 | | | 127 | |
128 | buf = fdt_getprop(fdtbus_get_data(), | | 128 | buf = fdt_getprop(fdtbus_get_data(), |
129 | fdtbus_phandle2offset(phandle), prop, &len); | | 129 | fdtbus_phandle2offset(phandle), prop, &len); |
130 | if (buf == NULL || len < sizeof(phandle_ref)) | | 130 | if (buf == NULL || len < sizeof(phandle_ref)) |
131 | return -1; | | 131 | return -1; |
132 | | | 132 | |
133 | phandle_ref = be32dec(buf); | | 133 | phandle_ref = be32dec(buf); |
134 | | | 134 | |
135 | return fdtbus_get_phandle_from_native(phandle_ref); | | 135 | return fdtbus_get_phandle_from_native(phandle_ref); |
136 | } | | 136 | } |
137 | | | 137 | |
138 | int | | 138 | int |
139 | fdtbus_get_phandle_with_data(int phandle, const char *prop, const char *cells, | | 139 | fdtbus_get_phandle_with_data(int phandle, const char *prop, const char *cells, |
140 | int index, struct fdt_phandle_data *data) | | 140 | int index, struct fdt_phandle_data *data) |
141 | { | | 141 | { |
142 | int len; | | 142 | int len; |
143 | const int offset = 1; | | 143 | const int offset = 1; |
144 | | | 144 | |
145 | const u_int *p = fdtbus_get_prop(phandle, prop, &len); | | 145 | const u_int *p = fdtbus_get_prop(phandle, prop, &len); |
146 | if (p == NULL || len <= 0) | | 146 | if (p == NULL || len <= 0) |
147 | return EINVAL; | | 147 | return EINVAL; |
148 | | | 148 | |
149 | for (int i = 0; len > 0; i++) { | | 149 | for (int i = 0; len > 0; i++) { |
150 | u_int phandle_ref = be32toh(*p); | | 150 | u_int phandle_ref = be32toh(*p); |
151 | const u_int iparent = fdtbus_get_phandle_from_native(phandle_ref); | | 151 | const u_int iparent = fdtbus_get_phandle_from_native(phandle_ref); |
152 | uint32_t cells_num; | | 152 | uint32_t cells_num; |
153 | of_getprop_uint32(iparent, cells, &cells_num); | | 153 | of_getprop_uint32(iparent, cells, &cells_num); |
154 | | | 154 | |
155 | if (index == i) { | | 155 | if (index == i) { |
156 | if (data != NULL) { | | 156 | if (data != NULL) { |
157 | data->phandle = iparent; | | 157 | data->phandle = iparent; |
158 | data->count = cells_num; | | 158 | data->count = cells_num; |
159 | data->values = p + offset; | | 159 | data->values = p + offset; |
160 | } | | 160 | } |
161 | goto done; | | 161 | goto done; |
162 | } | | 162 | } |
163 | | | 163 | |
164 | const u_int reclen = offset + cells_num; | | 164 | const u_int reclen = offset + cells_num; |
165 | len -= reclen * sizeof(u_int); | | 165 | len -= reclen * sizeof(u_int); |
166 | p += reclen; | | 166 | p += reclen; |
167 | } | | 167 | } |
168 | return EINVAL; | | 168 | return EINVAL; |
169 | | | 169 | |
170 | done: | | 170 | done: |
171 | return 0; | | 171 | return 0; |
172 | } | | 172 | } |
173 | | | 173 | |
174 | int | | 174 | int |
175 | fdtbus_get_phandle_from_native(int phandle) | | 175 | fdtbus_get_phandle_from_native(int phandle) |
176 | { | | 176 | { |
177 | const int off = fdt_node_offset_by_phandle(fdt_data, phandle); | | 177 | const int off = fdt_node_offset_by_phandle(fdt_data, phandle); |
178 | if (off < 0) { | | 178 | if (off < 0) { |
179 | return -1; | | 179 | return -1; |
180 | } | | 180 | } |
181 | return fdtbus_offset2phandle(off); | | 181 | return fdtbus_offset2phandle(off); |
182 | } | | 182 | } |
183 | | | 183 | |
184 | bool | | 184 | bool |
185 | fdtbus_get_path(int phandle, char *buf, size_t buflen) | | 185 | fdtbus_get_path(int phandle, char *buf, size_t buflen) |
186 | { | | 186 | { |
187 | const int off = fdtbus_phandle2offset(phandle); | | 187 | const int off = fdtbus_phandle2offset(phandle); |
188 | if (off < 0) { | | 188 | if (off < 0) { |
189 | return false; | | 189 | return false; |
190 | } | | 190 | } |
191 | if (fdt_get_path(fdt_data, off, buf, (int)buflen) != 0) { | | 191 | if (fdt_get_path(fdt_data, off, buf, (int)buflen) != 0) { |
192 | return false; | | 192 | return false; |
193 | } | | 193 | } |
194 | return true; | | 194 | return true; |
195 | } | | 195 | } |
196 | | | 196 | |
197 | uint64_t | | 197 | uint64_t |
198 | fdtbus_get_cells(const uint8_t *buf, int cells) | | 198 | fdtbus_get_cells(const uint8_t *buf, int cells) |
199 | { | | 199 | { |
200 | switch (cells) { | | 200 | switch (cells) { |
201 | case 0: return 0; | | 201 | case 0: return 0; |
202 | case 1: return be32dec(buf); | | 202 | case 1: return be32dec(buf); |
203 | case 2: return ((uint64_t)be32dec(buf)<<32)|be32dec(buf+4); | | 203 | case 2: return ((uint64_t)be32dec(buf)<<32)|be32dec(buf+4); |
204 | default: panic("fdtbus_get_cells: bad cells val %d\n", cells); | | 204 | default: panic("fdtbus_get_cells: bad cells val %d\n", cells); |
205 | } | | 205 | } |
206 | } | | 206 | } |
207 | | | 207 | |
208 | static uint64_t | | 208 | static uint64_t |
209 | fdtbus_decode_range(int phandle, uint64_t paddr) | | 209 | fdtbus_decode_range(int phandle, uint64_t paddr) |
210 | { | | 210 | { |
211 | const int parent = OF_parent(phandle); | | 211 | const int parent = OF_parent(phandle); |
212 | if (parent == -1) | | 212 | if (parent == -1) |
213 | return paddr; | | 213 | return paddr; |
214 | | | 214 | |
215 | if (!fdtbus_decoderegprop) | | 215 | if (!fdtbus_decoderegprop) |
216 | return paddr; | | 216 | return paddr; |
217 | | | 217 | |
218 | const uint8_t *buf; | | 218 | const uint8_t *buf; |
219 | int len; | | 219 | int len; |
220 | | | 220 | |
221 | buf = fdt_getprop(fdtbus_get_data(), | | 221 | buf = fdt_getprop(fdtbus_get_data(), |
222 | fdtbus_phandle2offset(phandle), "ranges", &len); | | 222 | fdtbus_phandle2offset(phandle), "ranges", &len); |
223 | if (buf == NULL) | | 223 | if (buf == NULL) |
224 | return paddr; | | 224 | return paddr; |
225 | | | 225 | |
226 | if (len == 0) { | | 226 | if (len == 0) { |
227 | /* pass through to parent */ | | 227 | /* pass through to parent */ |
228 | return fdtbus_decode_range(parent, paddr); | | 228 | return fdtbus_decode_range(parent, paddr); |
229 | } | | 229 | } |
230 | | | 230 | |
231 | const int addr_cells = fdtbus_get_addr_cells(phandle); | | 231 | const int addr_cells = fdtbus_get_addr_cells(phandle); |
232 | const int size_cells = fdtbus_get_size_cells(phandle); | | 232 | const int size_cells = fdtbus_get_size_cells(phandle); |
233 | const int paddr_cells = fdtbus_get_addr_cells(parent); | | 233 | const int paddr_cells = fdtbus_get_addr_cells(parent); |
234 | if (addr_cells == -1 || size_cells == -1 || paddr_cells == -1) | | 234 | if (addr_cells == -1 || size_cells == -1 || paddr_cells == -1) |
235 | return paddr; | | 235 | return paddr; |
236 | | | 236 | |
237 | while (len > 0) { | | 237 | while (len > 0) { |
238 | uint64_t cba, pba, cl; | | 238 | uint64_t cba, pba, cl; |
239 | cba = fdtbus_get_cells(buf, addr_cells); | | 239 | cba = fdtbus_get_cells(buf, addr_cells); |
240 | buf += addr_cells * 4; | | 240 | buf += addr_cells * 4; |
241 | pba = fdtbus_get_cells(buf, paddr_cells); | | 241 | pba = fdtbus_get_cells(buf, paddr_cells); |
242 | buf += paddr_cells * 4; | | 242 | buf += paddr_cells * 4; |
243 | cl = fdtbus_get_cells(buf, size_cells); | | 243 | cl = fdtbus_get_cells(buf, size_cells); |
244 | buf += size_cells * 4; | | 244 | buf += size_cells * 4; |
245 | | | 245 | |
246 | #ifdef FDTBUS_DEBUG | | 246 | #ifdef FDTBUS_DEBUG |
247 | printf("%s: %s: cba=%#" PRIx64 ", pba=%#" PRIx64 ", cl=%#" PRIx64 "\n", __func__, fdt_get_name(fdtbus_get_data(), fdtbus_phandle2offset(phandle), NULL), cba, pba, cl); | | 247 | printf("%s: %s: cba=%#" PRIx64 ", pba=%#" PRIx64 ", cl=%#" PRIx64 "\n", __func__, fdt_get_name(fdtbus_get_data(), fdtbus_phandle2offset(phandle), NULL), cba, pba, cl); |
248 | #endif | | 248 | #endif |
249 | | | 249 | |
250 | if (paddr >= cba && paddr < cba + cl) | | 250 | if (paddr >= cba && paddr < cba + cl) |
251 | return fdtbus_decode_range(parent, pba) + (paddr - cba); | | 251 | return fdtbus_decode_range(parent, pba) + (paddr - cba); |
252 | | | 252 | |
253 | len -= (addr_cells + paddr_cells + size_cells) * 4; | | 253 | len -= (addr_cells + paddr_cells + size_cells) * 4; |
254 | } | | 254 | } |
255 | | | 255 | |
256 | /* No mapping found */ | | 256 | /* No mapping found */ |
257 | return paddr; | | 257 | return paddr; |
258 | } | | 258 | } |
259 | | | 259 | |
260 | int | | 260 | int |
261 | fdtbus_get_reg_byname(int phandle, const char *name, bus_addr_t *paddr, | | 261 | fdtbus_get_reg_byname(int phandle, const char *name, bus_addr_t *paddr, |
262 | bus_size_t *psize) | | 262 | bus_size_t *psize) |
263 | { | | 263 | { |
264 | u_int index; | | 264 | u_int index; |
265 | int error; | | 265 | int error; |
266 | | | 266 | |
267 | error = fdtbus_get_index(phandle, "reg-names", name, &index); | | 267 | error = fdtbus_get_index(phandle, "reg-names", name, &index); |
268 | if (error != 0) | | 268 | if (error != 0) |
269 | return ENOENT; | | 269 | return ENOENT; |
270 | | | 270 | |
271 | return fdtbus_get_reg(phandle, index, paddr, psize); | | 271 | return fdtbus_get_reg(phandle, index, paddr, psize); |
272 | } | | 272 | } |
273 | | | 273 | |
274 | int | | 274 | int |
275 | fdtbus_get_reg(int phandle, u_int index, bus_addr_t *paddr, bus_size_t *psize) | | 275 | fdtbus_get_reg(int phandle, u_int index, bus_addr_t *paddr, bus_size_t *psize) |
276 | { | | 276 | { |
277 | uint64_t addr, size; | | 277 | uint64_t addr, size; |
278 | int error; | | 278 | int error; |
279 | | | 279 | |
280 | error = fdtbus_get_reg64(phandle, index, &addr, &size); | | 280 | error = fdtbus_get_reg64(phandle, index, &addr, &size); |
281 | if (error) | | 281 | if (error) |
282 | return error; | | 282 | return error; |
283 | | | 283 | |
284 | if (sizeof(bus_addr_t) == 4 && (addr + size) > 0x100000000) | | 284 | if (sizeof(bus_addr_t) == 4 && (addr + size) > 0x100000000) |
285 | return ERANGE; | | 285 | return ERANGE; |
286 | | | 286 | |
287 | if (paddr) | | 287 | if (paddr) |
288 | *paddr = (bus_addr_t)addr; | | 288 | *paddr = (bus_addr_t)addr; |
289 | if (psize) | | 289 | if (psize) |
290 | *psize = (bus_size_t)size; | | 290 | *psize = (bus_size_t)size; |
291 | | | 291 | |
292 | return 0; | | 292 | return 0; |
293 | } | | 293 | } |
294 | | | 294 | |
295 | int | | 295 | int |
296 | fdtbus_get_reg64(int phandle, u_int index, uint64_t *paddr, uint64_t *psize) | | 296 | fdtbus_get_reg64(int phandle, u_int index, uint64_t *paddr, uint64_t *psize) |
297 | { | | 297 | { |
298 | uint64_t addr, size; | | 298 | uint64_t addr, size; |
299 | const uint8_t *buf; | | 299 | const uint8_t *buf; |
300 | int len; | | 300 | int len; |
301 | | | 301 | |
302 | const int addr_cells = fdtbus_get_addr_cells(OF_parent(phandle)); | | 302 | const int addr_cells = fdtbus_get_addr_cells(OF_parent(phandle)); |
303 | const int size_cells = fdtbus_get_size_cells(OF_parent(phandle)); | | 303 | const int size_cells = fdtbus_get_size_cells(OF_parent(phandle)); |
304 | if (addr_cells == -1 || size_cells == -1) | | 304 | if (addr_cells == -1 || size_cells == -1) |
305 | return EINVAL; | | 305 | return EINVAL; |
306 | | | 306 | |
307 | buf = fdt_getprop(fdtbus_get_data(), | | 307 | buf = fdt_getprop(fdtbus_get_data(), |
308 | fdtbus_phandle2offset(phandle), "reg", &len); | | 308 | fdtbus_phandle2offset(phandle), "reg", &len); |
309 | if (buf == NULL || len <= 0) | | 309 | if (buf == NULL || len <= 0) |
310 | return EINVAL; | | 310 | return EINVAL; |
311 | | | 311 | |
312 | const u_int reglen = size_cells * 4 + addr_cells * 4; | | 312 | const u_int reglen = size_cells * 4 + addr_cells * 4; |
313 | if (reglen == 0) | | 313 | if (reglen == 0) |
314 | return EINVAL; | | 314 | return EINVAL; |
315 | | | 315 | |
316 | if (index >= len / reglen) | | 316 | if (index >= len / reglen) |
317 | return ENXIO; | | 317 | return ENXIO; |
318 | | | 318 | |
319 | buf += index * reglen; | | 319 | buf += index * reglen; |
320 | addr = fdtbus_get_cells(buf, addr_cells); | | 320 | addr = fdtbus_get_cells(buf, addr_cells); |
321 | buf += addr_cells * 4; | | 321 | buf += addr_cells * 4; |
322 | size = fdtbus_get_cells(buf, size_cells); | | 322 | size = fdtbus_get_cells(buf, size_cells); |
323 | | | 323 | |
324 | if (paddr) { | | 324 | if (paddr) { |
325 | *paddr = fdtbus_decode_range(OF_parent(phandle), addr); | | 325 | *paddr = fdtbus_decode_range(OF_parent(phandle), addr); |
326 | #ifdef FDTBUS_DEBUG | | 326 | #ifdef FDTBUS_DEBUG |
327 | const char *name = fdt_get_name(fdtbus_get_data(), | | 327 | const char *name = fdt_get_name(fdtbus_get_data(), |
328 | fdtbus_phandle2offset(phandle), NULL); | | 328 | fdtbus_phandle2offset(phandle), NULL); |
329 | printf("fdt: [%s] decoded addr #%u: %" PRIx64 | | 329 | printf("fdt: [%s] decoded addr #%u: %" PRIx64 |
330 | " -> %" PRIx64 "\n", name, index, addr, *paddr); | | 330 | " -> %" PRIx64 "\n", name, index, addr, *paddr); |
331 | #endif | | 331 | #endif |
332 | } | | 332 | } |
333 | if (psize) | | 333 | if (psize) |
334 | *psize = size; | | 334 | *psize = size; |
335 | | | 335 | |
336 | return 0; | | 336 | return 0; |
337 | } | | 337 | } |
338 | | | 338 | |
339 | #if defined(FDT) | | 339 | #if defined(FDT) |
340 | const struct fdt_console * | | 340 | const struct fdt_console * |
341 | fdtbus_get_console(void) | | 341 | fdtbus_get_console(void) |
342 | { | | 342 | { |
343 | static const struct fdt_console_info *booted_console = NULL; | | 343 | static const struct fdt_console_info *booted_console = NULL; |
344 | | | 344 | |
345 | if (booted_console == NULL) { | | 345 | if (booted_console == NULL) { |
346 | __link_set_decl(fdt_consoles, struct fdt_console_info); | | 346 | __link_set_decl(fdt_consoles, struct fdt_console_info); |
347 | struct fdt_console_info * const *info; | | 347 | struct fdt_console_info * const *info; |
348 | const struct fdt_console_info *best_info = NULL; | | 348 | const struct fdt_console_info *best_info = NULL; |
349 | const int phandle = fdtbus_get_stdout_phandle(); | | 349 | const int phandle = fdtbus_get_stdout_phandle(); |
350 | int best_match = 0; | | 350 | int best_match = 0; |
351 | | | 351 | |
352 | if (phandle == -1) { | | 352 | if (phandle == -1) { |
353 | printf("WARNING: no console device\n"); | | 353 | printf("WARNING: no console device\n"); |
354 | return NULL; | | 354 | return NULL; |
355 | } | | 355 | } |
356 | | | 356 | |
357 | __link_set_foreach(info, fdt_consoles) { | | 357 | __link_set_foreach(info, fdt_consoles) { |
358 | const int match = (*info)->ops->match(phandle); | | 358 | const int match = (*info)->ops->match(phandle); |
359 | if (match > best_match) { | | 359 | if (match > best_match) { |
360 | best_match = match; | | 360 | best_match = match; |
361 | best_info = *info; | | 361 | best_info = *info; |
362 | } | | 362 | } |
363 | } | | 363 | } |
364 | | | 364 | |
365 | booted_console = best_info; | | 365 | booted_console = best_info; |
366 | } | | 366 | } |
367 | | | 367 | |
368 | return booted_console == NULL ? NULL : booted_console->ops; | | 368 | return booted_console == NULL ? NULL : booted_console->ops; |
369 | } | | 369 | } |
370 | #endif | | 370 | #endif |
371 | | | 371 | |
372 | const char * | | 372 | const char * |
373 | fdtbus_get_stdout_path(void) | | 373 | fdtbus_get_stdout_path(void) |
374 | { | | 374 | { |
375 | const char *prop; | | 375 | const char *prop; |
376 | | | 376 | |
377 | const int off = fdt_path_offset(fdtbus_get_data(), "/chosen"); | | 377 | const int off = fdt_path_offset(fdtbus_get_data(), "/chosen"); |
378 | if (off >= 0) { | | 378 | if (off >= 0) { |
379 | prop = fdt_getprop(fdtbus_get_data(), off, "stdout-path", NULL); | | 379 | prop = fdt_getprop(fdtbus_get_data(), off, "stdout-path", NULL); |
380 | if (prop != NULL) | | 380 | if (prop != NULL) |
381 | return prop; | | 381 | return prop; |
382 | } | | 382 | } |
383 | | | 383 | |
384 | /* If the stdout-path property is not found, return the default */ | | 384 | /* If the stdout-path property is not found, return the default */ |
385 | return FDT_DEFAULT_STDOUT_PATH; | | 385 | return FDT_DEFAULT_STDOUT_PATH; |
386 | } | | 386 | } |
387 | | | 387 | |
388 | int | | 388 | int |
389 | fdtbus_get_stdout_phandle(void) | | 389 | fdtbus_get_stdout_phandle(void) |
390 | { | | 390 | { |
391 | const char *prop, *p; | | 391 | const char *prop, *p; |
392 | int off, len; | | 392 | int off, len; |
393 | | | 393 | |
394 | prop = fdtbus_get_stdout_path(); | | 394 | prop = fdtbus_get_stdout_path(); |
395 | if (prop == NULL) | | 395 | if (prop == NULL) |
396 | return -1; | | 396 | return -1; |
397 | | | 397 | |
398 | p = strchr(prop, ':'); | | 398 | p = strchr(prop, ':'); |
399 | len = p == NULL ? strlen(prop) : (p - prop); | | 399 | len = p == NULL ? strlen(prop) : (p - prop); |
400 | if (*prop != '/') { | | 400 | if (*prop != '/') { |
401 | /* Alias */ | | 401 | /* Alias */ |
402 | prop = fdt_get_alias_namelen(fdtbus_get_data(), prop, len); | | 402 | prop = fdt_get_alias_namelen(fdtbus_get_data(), prop, len); |
403 | if (prop == NULL) | | 403 | if (prop == NULL) |
404 | return -1; | | 404 | return -1; |
405 | len = strlen(prop); | | 405 | len = strlen(prop); |
406 | } | | 406 | } |
407 | off = fdt_path_offset_namelen(fdtbus_get_data(), prop, len); | | 407 | off = fdt_path_offset_namelen(fdtbus_get_data(), prop, len); |
408 | if (off < 0) | | 408 | if (off < 0) |
409 | return -1; | | 409 | return -1; |
410 | | | 410 | |
411 | return fdtbus_offset2phandle(off); | | 411 | return fdtbus_offset2phandle(off); |
412 | } | | 412 | } |
413 | | | 413 | |
414 | int | | 414 | int |
415 | fdtbus_get_stdout_speed(void) | | 415 | fdtbus_get_stdout_speed(void) |
416 | { | | 416 | { |
417 | const char *prop, *p; | | 417 | const char *prop, *p; |
418 | | | 418 | |
419 | prop = fdtbus_get_stdout_path(); | | 419 | prop = fdtbus_get_stdout_path(); |
420 | if (prop == NULL) | | 420 | if (prop == NULL) |
421 | return -1; | | 421 | return -1; |
422 | | | 422 | |
423 | p = strchr(prop, ':'); | | 423 | p = strchr(prop, ':'); |
424 | if (p == NULL) | | 424 | if (p == NULL) |
425 | return -1; | | 425 | return -1; |
426 | | | 426 | |
427 | return (int)strtoul(p + 1, NULL, 10); | | 427 | return (int)strtoul(p + 1, NULL, 10); |
428 | } | | 428 | } |
429 | | | 429 | |
430 | tcflag_t | | 430 | tcflag_t |
431 | fdtbus_get_stdout_flags(void) | | 431 | fdtbus_get_stdout_flags(void) |
432 | { | | 432 | { |
433 | const char *prop, *p; | | 433 | const char *prop, *p; |
434 | tcflag_t flags = TTYDEF_CFLAG; | | 434 | tcflag_t flags = TTYDEF_CFLAG; |
435 | char *ep; | | 435 | char *ep; |
436 | | | 436 | |
437 | prop = fdtbus_get_stdout_path(); | | 437 | prop = fdtbus_get_stdout_path(); |
438 | if (prop == NULL) | | 438 | if (prop == NULL) |
439 | return flags; | | 439 | return flags; |
440 | | | 440 | |
441 | p = strchr(prop, ':'); | | 441 | p = strchr(prop, ':'); |
442 | if (p == NULL) | | 442 | if (p == NULL) |
443 | return flags; | | 443 | return flags; |
444 | | | 444 | |
445 | ep = NULL; | | 445 | ep = NULL; |
446 | (void)strtoul(p + 1, &ep, 10); | | 446 | (void)strtoul(p + 1, &ep, 10); |
447 | if (ep == NULL) | | 447 | if (ep == NULL) |
448 | return flags; | | 448 | return flags; |
449 | | | 449 | |
450 | /* <baud>{<parity>{<bits>{<flow>}}} */ | | 450 | /* <baud>{<parity>{<bits>{<flow>}}} */ |
451 | while (*ep) { | | 451 | while (*ep) { |
452 | switch (*ep) { | | 452 | switch (*ep) { |
453 | /* parity */ | | 453 | /* parity */ |
454 | case 'n': flags &= ~(PARENB|PARODD); break; | | 454 | case 'n': flags &= ~(PARENB|PARODD); break; |
455 | case 'e': flags &= ~PARODD; flags |= PARENB; break; | | 455 | case 'e': flags &= ~PARODD; flags |= PARENB; break; |
456 | case 'o': flags |= (PARENB|PARODD); break; | | 456 | case 'o': flags |= (PARENB|PARODD); break; |
457 | /* bits */ | | 457 | /* bits */ |
458 | case '5': flags &= ~CSIZE; flags |= CS5; break; | | 458 | case '5': flags &= ~CSIZE; flags |= CS5; break; |
459 | case '6': flags &= ~CSIZE; flags |= CS6; break; | | 459 | case '6': flags &= ~CSIZE; flags |= CS6; break; |
460 | case '7': flags &= ~CSIZE; flags |= CS7; break; | | 460 | case '7': flags &= ~CSIZE; flags |= CS7; break; |
461 | case '8': flags &= ~CSIZE; flags |= CS8; break; | | 461 | case '8': flags &= ~CSIZE; flags |= CS8; break; |
462 | /* flow */ | | 462 | /* flow */ |
463 | case 'r': flags |= CRTSCTS; break; | | 463 | case 'r': flags |= CRTSCTS; break; |
464 | } | | 464 | } |
465 | ep++; | | 465 | ep++; |
466 | } | | 466 | } |
467 | | | 467 | |
468 | return flags; | | 468 | return flags; |
469 | } | | 469 | } |
470 | | | 470 | |
471 | bool | | 471 | bool |
472 | fdtbus_status_okay(int phandle) | | 472 | fdtbus_status_okay(int phandle) |
473 | { | | 473 | { |
474 | const int off = fdtbus_phandle2offset(phandle); | | 474 | const int off = fdtbus_phandle2offset(phandle); |
475 | | | 475 | |
476 | const char *prop = fdt_getprop(fdtbus_get_data(), off, "status", NULL); | | 476 | const char *prop = fdt_getprop(fdtbus_get_data(), off, "status", NULL); |
477 | if (prop == NULL) | | 477 | if (prop == NULL) |
478 | return true; | | 478 | return true; |
479 | | | 479 | |
480 | return strncmp(prop, "ok", 2) == 0; | | 480 | return strncmp(prop, "ok", 2) == 0; |
481 | } | | 481 | } |
482 | | | 482 | |
483 | const void * | | 483 | const void * |
484 | fdtbus_get_prop(int phandle, const char *prop, int *plen) | | 484 | fdtbus_get_prop(int phandle, const char *prop, int *plen) |
485 | { | | 485 | { |
486 | const int off = fdtbus_phandle2offset(phandle); | | 486 | const int off = fdtbus_phandle2offset(phandle); |
487 | | | 487 | |
488 | return fdt_getprop(fdtbus_get_data(), off, prop, plen); | | 488 | return fdt_getprop(fdtbus_get_data(), off, prop, plen); |
489 | } | | 489 | } |
490 | | | 490 | |
491 | const char * | | 491 | const char * |
492 | fdtbus_get_string(int phandle, const char *prop) | | 492 | fdtbus_get_string(int phandle, const char *prop) |
493 | { | | 493 | { |
494 | const int off = fdtbus_phandle2offset(phandle); | | 494 | const int off = fdtbus_phandle2offset(phandle); |
495 | | | 495 | |
496 | if (strcmp(prop, "name") == 0) | | 496 | if (strcmp(prop, "name") == 0) |
497 | return fdt_get_name(fdtbus_get_data(), off, NULL); | | 497 | return fdt_get_name(fdtbus_get_data(), off, NULL); |
498 | else | | 498 | else |
499 | return fdt_getprop(fdtbus_get_data(), off, prop, NULL); | | 499 | return fdt_getprop(fdtbus_get_data(), off, prop, NULL); |
500 | } | | 500 | } |
501 | | | 501 | |
502 | const char * | | 502 | const char * |
503 | fdtbus_get_string_index(int phandle, const char *prop, u_int index) | | 503 | fdtbus_get_string_index(int phandle, const char *prop, u_int index) |
504 | { | | 504 | { |
505 | const char *names, *name; | | 505 | const char *names; |
506 | int len, cur; | | 506 | int len; |
507 | | | 507 | |
508 | if ((len = OF_getproplen(phandle, prop)) < 0) | | 508 | if ((len = OF_getproplen(phandle, prop)) < 0) |
509 | return NULL; | | 509 | return NULL; |
510 | | | 510 | |
511 | names = fdtbus_get_string(phandle, prop); | | 511 | names = fdtbus_get_string(phandle, prop); |
512 | | | 512 | |
513 | for (name = names, cur = 0; len > 0; | | 513 | return strlist_string(names, len, index); |
514 | len -= strlen(name) + 1, name += strlen(name) + 1, cur++) { | | | |
515 | if (index == cur) | | | |
516 | return name; | | | |
517 | } | | | |
518 | | | | |
519 | return NULL; | | | |
520 | } | | 514 | } |
521 | | | 515 | |
522 | int | | 516 | int |
523 | fdtbus_get_index(int phandle, const char *prop, const char *name, u_int *idx) | | 517 | fdtbus_get_index(int phandle, const char *prop, const char *name, u_int *idx) |
524 | { | | 518 | { |
525 | const char *p; | | 519 | const char *p; |
526 | size_t pl; | | 520 | int len, index; |
527 | u_int index; | | | |
528 | int len; | | | |
529 | | | 521 | |
530 | p = fdtbus_get_prop(phandle, prop, &len); | | 522 | p = fdtbus_get_prop(phandle, prop, &len); |
531 | if (p == NULL || len <= 0) | | 523 | if (p == NULL || len <= 0) |
532 | return -1; | | 524 | return -1; |
533 | | | 525 | |
534 | for (index = 0; len > 0; | | 526 | index = strlist_index(p, len, name); |
535 | pl = strlen(p) + 1, len -= pl, p += pl, index++) { | | 527 | if (index == -1) |
536 | if (strcmp(p, name) == 0) { | | 528 | return -1; |
537 | *idx = index; | | | |
538 | return 0; | | | |
539 | } | | | |
540 | } | | | |
541 | | | 529 | |
542 | return -1; | | 530 | *idx = index; |
| | | 531 | return 0; |
543 | } | | 532 | } |