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, &regs[0], &regs[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, &regs[0], &regs[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