1 /*
2 * Copyright (c) 2006-2022, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2022-08-25 GuEe-GUI first version
9 */
10
11 #include <rtthread.h>
12
13 #include <drivers/ofw.h>
14 #include <drivers/ofw_io.h>
15 #include <drivers/ofw_fdt.h>
16 #include <drivers/misc.h>
17
18 #define DBG_TAG "rtdm.ofw"
19 #define DBG_LVL DBG_INFO
20 #include <rtdbg.h>
21
22 #include "ofw_internal.h"
23
24 static volatile rt_atomic_t _bus_ranges_idx = 0;
25 static struct bus_ranges *_bus_ranges[RT_USING_OFW_BUS_RANGES_NUMBER] = {};
26
ofw_bus_addr_cells(struct rt_ofw_node * np)27 static int ofw_bus_addr_cells(struct rt_ofw_node *np)
28 {
29 int res = OFW_ROOT_NODE_ADDR_CELLS_DEFAULT;
30
31 for (rt_uint32_t cells; np; np = np->parent)
32 {
33 if (!rt_ofw_prop_read_u32(np, "#address-cells", &cells))
34 {
35 res = cells;
36 break;
37 }
38 }
39
40 return res;
41 }
42
ofw_bus_size_cells(struct rt_ofw_node * np)43 static int ofw_bus_size_cells(struct rt_ofw_node *np)
44 {
45 int res = OFW_ROOT_NODE_SIZE_CELLS_DEFAULT;
46
47 for (rt_uint32_t cells; np; np = np->parent)
48 {
49 if (!rt_ofw_prop_read_u32(np, "#size-cells", &cells))
50 {
51 res = cells;
52 break;
53 }
54 }
55
56 return res;
57 }
58
rt_ofw_bus_addr_cells(struct rt_ofw_node * np)59 int rt_ofw_bus_addr_cells(struct rt_ofw_node *np)
60 {
61 return np ? ofw_bus_addr_cells(np) : -RT_EINVAL;
62 }
63
rt_ofw_bus_size_cells(struct rt_ofw_node * np)64 int rt_ofw_bus_size_cells(struct rt_ofw_node *np)
65 {
66 return np ? ofw_bus_size_cells(np) : -RT_EINVAL;
67 }
68
rt_ofw_io_addr_cells(struct rt_ofw_node * np)69 int rt_ofw_io_addr_cells(struct rt_ofw_node *np)
70 {
71 return np ? ofw_bus_addr_cells(np->parent ? np->parent : np) : -RT_EINVAL;
72 }
73
rt_ofw_io_size_cells(struct rt_ofw_node * np)74 int rt_ofw_io_size_cells(struct rt_ofw_node *np)
75 {
76 return np ? ofw_bus_size_cells(np->parent ? np->parent : np) : -RT_EINVAL;
77 }
78
rt_ofw_get_address_count(struct rt_ofw_node * np)79 int rt_ofw_get_address_count(struct rt_ofw_node *np)
80 {
81 int count;
82
83 if (np)
84 {
85 rt_ssize_t len;
86
87 count = 0;
88
89 if (rt_ofw_get_prop(np, "reg", &len))
90 {
91 count = len / (sizeof(fdt32_t) * (rt_ofw_io_addr_cells(np) + rt_ofw_io_size_cells(np)));
92 }
93 }
94 else
95 {
96 count = -RT_EINVAL;
97 }
98
99 return count;
100 }
101
ofw_get_address(struct rt_ofw_node * np,int index,rt_uint64_t * out_address,rt_uint64_t * out_size)102 static rt_err_t ofw_get_address(struct rt_ofw_node *np, int index, rt_uint64_t *out_address, rt_uint64_t *out_size)
103 {
104 rt_ssize_t len;
105 rt_err_t err = RT_EOK;
106 int addr_cells = rt_ofw_io_addr_cells(np);
107 int size_cells = rt_ofw_io_size_cells(np);
108 int skip_cells = (addr_cells + size_cells) * index;
109 const fdt32_t *cell = rt_ofw_prop_read_raw(np, "reg", &len);
110
111 if (cell && skip_cells < (len / sizeof(*cell)))
112 {
113 cell += skip_cells;
114 *out_address = rt_fdt_next_cell(&cell, addr_cells);
115 *out_address = rt_ofw_translate_address(np, RT_NULL, *out_address);
116 *out_size = rt_fdt_read_number(cell, size_cells);
117 }
118 else
119 {
120 err = -RT_EINVAL;
121 }
122
123 return err;
124 }
125
rt_ofw_get_address(struct rt_ofw_node * np,int index,rt_uint64_t * out_address,rt_uint64_t * out_size)126 rt_err_t rt_ofw_get_address(struct rt_ofw_node *np, int index, rt_uint64_t *out_address, rt_uint64_t *out_size)
127 {
128 rt_err_t err;
129
130 if (np && index >= 0 && (out_address || out_size))
131 {
132 rt_uint64_t address, size;
133
134 err = ofw_get_address(np, index, &address, &size);
135
136 if (!err)
137 {
138 if (out_address)
139 {
140 *out_address = address;
141 }
142 if (out_size)
143 {
144 *out_size = size;
145 }
146 }
147 }
148 else
149 {
150 err = -RT_EINVAL;
151 }
152
153 return err;
154 }
155
ofw_get_address_by_name(struct rt_ofw_node * np,const char * name,rt_uint64_t * out_address,rt_uint64_t * out_size)156 static rt_err_t ofw_get_address_by_name(struct rt_ofw_node *np, const char *name,
157 rt_uint64_t *out_address, rt_uint64_t *out_size)
158
159 {
160 int index = 0;
161 rt_err_t err = -RT_EEMPTY;
162 const char *reg_name;
163 struct rt_ofw_prop *prop;
164
165 rt_ofw_foreach_prop_string(np, "reg-names", prop, reg_name)
166 {
167 if (!rt_strcmp(name, reg_name))
168 {
169 err = rt_ofw_get_address(np, index, out_address, out_size);
170
171 break;
172 }
173
174 ++index;
175 }
176
177 return err;
178 }
179
rt_ofw_get_address_by_name(struct rt_ofw_node * np,const char * name,rt_uint64_t * out_address,rt_uint64_t * out_size)180 rt_err_t rt_ofw_get_address_by_name(struct rt_ofw_node *np, const char *name,
181 rt_uint64_t *out_address, rt_uint64_t *out_size)
182 {
183 rt_err_t err;
184
185 if (np && name && (out_address || out_size))
186 {
187 rt_uint64_t address, size;
188
189 err = ofw_get_address_by_name(np, name, &address, &size);
190
191 if (!err)
192 {
193 if (out_address)
194 {
195 *out_address = address;
196 }
197 if (out_size)
198 {
199 *out_size = size;
200 }
201 }
202 }
203 else
204 {
205 err = -RT_EINVAL;
206 }
207
208 return err;
209 }
210
rt_ofw_get_address_array(struct rt_ofw_node * np,int nr,rt_uint64_t * out_regs)211 int rt_ofw_get_address_array(struct rt_ofw_node *np, int nr, rt_uint64_t *out_regs)
212 {
213 int count;
214
215 if (np && nr > 0 && out_regs)
216 {
217 rt_ssize_t len;
218 int max_nr;
219 int addr_cells = rt_ofw_io_addr_cells(np);
220 int size_cells = rt_ofw_io_size_cells(np);
221 const fdt32_t *cell = rt_ofw_prop_read_raw(np, "reg", &len);
222
223 max_nr = len / (sizeof(*cell) * (addr_cells + size_cells));
224
225 if (nr > max_nr)
226 {
227 nr = max_nr;
228 }
229
230 count = nr;
231
232 while (nr --> 0)
233 {
234 *out_regs = rt_fdt_next_cell(&cell, addr_cells);
235 *out_regs = rt_ofw_translate_address(np, RT_NULL, *out_regs);
236 ++out_regs;
237
238 *out_regs = rt_fdt_next_cell(&cell, size_cells);
239 ++out_regs;
240 }
241 }
242 else
243 {
244 count = -RT_EINVAL;
245 }
246
247 return count;
248 }
249
ofw_bus_ranges(struct rt_ofw_node * np,struct rt_ofw_prop * prop)250 static struct bus_ranges *ofw_bus_ranges(struct rt_ofw_node *np, struct rt_ofw_prop *prop)
251 {
252 int id;
253 const fdt32_t *cell;
254 struct bus_ranges *ranges = RT_NULL;
255 int child_address_cells, child_size_cells, parent_address_cells, groups;
256 rt_uint64_t *child_addr, *parent_addr, *child_size;
257
258 /*
259 * Address Translation Example:
260 *
261 * / {
262 * #address-cells = <1>;
263 * #size-cells = <1>;
264 *
265 * soc {
266 * compatible = "simple-bus";
267 * #address-cells = <1>;
268 * #size-cells = <1>;
269 * ranges = <0x0 0xe0000000 0x00100000>;
270 *
271 * serial@4600 {
272 * device_type = "serial";
273 * reg = <0x4600 0x100>;
274 * clock-frequency = <0>;
275 * };
276 * };
277 * }
278 *
279 * The soc node specifies a ranges property of <0x0 0xe0000000 0x00100000>;
280 * This property value specifies that for a 1024 KB range of address space, a
281 * child node addressed at physical 0x0 maps to a parent address of physical
282 * 0xe0000000. With this mapping, the serial device node can be addressed by a
283 * load or store at address 0xe0004600, an offset of 0x4600 (specified in reg)
284 * plus the 0xe0000000 mapping specified in ranges:
285 *
286 * bus-address = parent-bus-address + (reg-address - child-bus-address)
287 */
288
289 do {
290 child_address_cells = rt_ofw_bus_addr_cells(np);
291 child_size_cells = rt_ofw_bus_size_cells(np);
292 parent_address_cells = rt_ofw_io_addr_cells(np);
293
294 if (child_address_cells < 0 || child_size_cells < 0 || parent_address_cells < 0)
295 {
296 LOG_D("%s read address/size cells fail: child[%d, %d] parent[%d]",
297 np->full_name, child_address_cells, child_size_cells, parent_address_cells);
298
299 break;
300 }
301
302 groups = prop->length / sizeof(*cell);
303 groups /= child_address_cells + child_size_cells + parent_address_cells;
304
305 ranges = rt_malloc(sizeof(*ranges) + sizeof(rt_uint64_t) * 3 * groups);
306
307 if (!ranges)
308 {
309 break;
310 }
311
312 ranges->nr = groups;
313 ranges->child_addr = (void *)ranges + sizeof(*ranges);
314 ranges->parent_addr = &ranges->child_addr[groups];
315 ranges->child_size = &ranges->parent_addr[groups];
316
317 cell = prop->value;
318
319 child_addr = ranges->child_addr;
320 parent_addr = ranges->parent_addr;
321 child_size = ranges->child_size;
322
323 while (groups --> 0)
324 {
325 *child_addr++ = rt_fdt_next_cell(&cell, child_address_cells);
326 *parent_addr++ = rt_fdt_next_cell(&cell, parent_address_cells);
327 *child_size++ = rt_fdt_next_cell(&cell, child_size_cells);
328 }
329
330 ranges->np = np;
331
332 id = (int)rt_atomic_add(&_bus_ranges_idx, 1);
333 RT_ASSERT(id < RT_ARRAY_SIZE(_bus_ranges));
334
335 _bus_ranges[id] = ranges;
336 } while (0);
337
338 return ranges;
339 }
340
rt_ofw_translate_address(struct rt_ofw_node * np,const char * range_type,rt_uint64_t address)341 rt_uint64_t rt_ofw_translate_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address)
342 {
343 rt_uint64_t cpu_addr = address;
344
345 if (!range_type)
346 {
347 range_type = "ranges";
348 }
349
350 rt_ofw_foreach_parent_node(np)
351 {
352 rt_ssize_t len;
353 struct rt_ofw_prop *prop;
354 struct bus_ranges *ranges = RT_NULL;
355
356 prop = rt_ofw_get_prop(np, range_type, &len);
357
358 if (!prop || !len)
359 {
360 continue;
361 }
362
363 for (int i = 0; i < RT_ARRAY_SIZE(_bus_ranges); ++i)
364 {
365 if (!_bus_ranges[i])
366 {
367 break;
368 }
369
370 if (_bus_ranges[i]->np == np)
371 {
372 ranges = _bus_ranges[i];
373 break;
374 }
375 }
376
377 if (!ranges)
378 {
379 ranges = ofw_bus_ranges(np, prop);
380 }
381
382 if (ranges)
383 {
384 for (int i = 0; i < ranges->nr; ++i)
385 {
386 rt_uint64_t child_addr = ranges->child_addr[i];
387 rt_uint64_t child_size = ranges->child_size[i];
388
389 if (address >= child_addr && address < child_addr + child_size)
390 {
391 cpu_addr = address + (ranges->parent_addr[i] - child_addr);
392
393 break;
394 }
395 }
396 }
397 else
398 {
399 cpu_addr = ~0ULL;
400 }
401
402 rt_ofw_node_put(np);
403
404 break;
405 }
406
407 return cpu_addr;
408 }
409
rt_ofw_reverse_address(struct rt_ofw_node * np,const char * range_type,rt_uint64_t address)410 rt_uint64_t rt_ofw_reverse_address(struct rt_ofw_node *np, const char *range_type, rt_uint64_t address)
411 {
412 rt_uint64_t bus_addr = address;
413
414 if (!range_type)
415 {
416 range_type = "ranges";
417 }
418
419 rt_ofw_foreach_parent_node(np)
420 {
421 rt_ssize_t len;
422 struct rt_ofw_prop *prop;
423 struct bus_ranges *ranges = RT_NULL;
424
425 prop = rt_ofw_get_prop(np, range_type, &len);
426
427 if (!prop || !len)
428 {
429 continue;
430 }
431
432 for (int i = 0; i < RT_ARRAY_SIZE(_bus_ranges); ++i)
433 {
434 if (!_bus_ranges[i])
435 {
436 break;
437 }
438
439 if (_bus_ranges[i]->np == np)
440 {
441 ranges = _bus_ranges[i];
442 break;
443 }
444 }
445
446 if (!ranges)
447 {
448 ranges = ofw_bus_ranges(np, prop);
449 }
450
451 if (ranges)
452 {
453 for (int i = 0; i < ranges->nr; ++i)
454 {
455 rt_uint64_t parent_addr = ranges->parent_addr[i];
456 rt_uint64_t child_size = ranges->child_size[i];
457
458 if (address >= parent_addr && address < parent_addr + child_size)
459 {
460 bus_addr = ranges->child_addr[i] + (address - parent_addr);
461
462 break;
463 }
464 }
465 }
466 else
467 {
468 bus_addr = ~0ULL;
469 }
470
471 rt_ofw_node_put(np);
472
473 break;
474 }
475
476 return bus_addr;
477 }
478
479 #ifdef ARCH_CPU_64BIT
480 #define ofw_address_cpu_cast(np, address) (void *)(address)
481 #else
482 #define ofw_address_cpu_cast(np, address) \
483 ({ \
484 if (((address) >> 32)) \
485 { \
486 LOG_W("%s find 64 bits address = %x%x", \
487 rt_ofw_node_full_name(np), \
488 ofw_static_cast(rt_ubase_t, (address) >> 32), \
489 ofw_static_cast(rt_ubase_t, (address))); \
490 } \
491 (void *)ofw_static_cast(rt_ubase_t, (address)); \
492 })
493 #endif
494
rt_ofw_iomap(struct rt_ofw_node * np,int index)495 void *rt_ofw_iomap(struct rt_ofw_node *np, int index)
496 {
497 void *iomem = RT_NULL;
498
499 if (np)
500 {
501 rt_uint64_t regs[2];
502
503 if (!ofw_get_address(np, index, ®s[0], ®s[1]))
504 {
505 iomem = rt_ioremap(ofw_address_cpu_cast(np, regs[0]), (size_t)regs[1]);
506 }
507 }
508
509 return iomem;
510 }
511
rt_ofw_iomap_by_name(struct rt_ofw_node * np,const char * name)512 void *rt_ofw_iomap_by_name(struct rt_ofw_node *np, const char *name)
513 {
514 void *iomem = RT_NULL;
515
516 if (np)
517 {
518 rt_uint64_t regs[2];
519
520 if (!ofw_get_address_by_name(np, name, ®s[0], ®s[1]))
521 {
522 iomem = rt_ioremap(ofw_address_cpu_cast(np, regs[0]), (size_t)regs[1]);
523 }
524 }
525
526 return iomem;
527 }
528