1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "dtb_node.h"
7 #include "libfdt.h"
8 #include "libfdt_env.h"
9 #include <ctype.h>
10
_parse_integer_fixup_radix(const char * s,unsigned int * base)11 static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
12 {
13 if (*base == 0)
14 {
15 if (s[0] == '0')
16 {
17 if (tolower(s[1]) == 'x' && isxdigit((int)s[2]))
18 *base = 16;
19 else
20 *base = 8;
21 }
22 else
23 *base = 10;
24 }
25 if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x')
26 s += 2;
27 return s;
28 }
29
simple_strtoul(const char * cp,char ** endp,unsigned int base)30 unsigned long simple_strtoul(const char *cp, char **endp,
31 unsigned int base)
32 {
33 unsigned long result = 0;
34 unsigned long value;
35
36 cp = _parse_integer_fixup_radix(cp, &base);
37
38 while (isxdigit((int)*cp) && (value = isdigit((int)*cp) ? *cp-'0' : (islower((int)*cp)
39 ? toupper(*cp) : *cp)-'A'+10) < base)
40 {
41 result = result*base + value;
42 cp++;
43 }
44
45 if (endp)
46 *endp = (char *)cp;
47
48 return result;
49 }
50
strict_strtoul(const char * cp,unsigned int base,unsigned long * res)51 int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
52 {
53 char *tail;
54 unsigned long val;
55 size_t len;
56
57 *res = 0;
58 len = strlen(cp);
59 if (len == 0)
60 return -EINVAL;
61
62 val = simple_strtoul(cp, &tail, base);
63 if (tail == cp)
64 return -EINVAL;
65
66 if ((*tail == '\0') ||
67 ((len == (size_t)(tail - cp) + 1) && (*tail == '\n')))
68 {
69 *res = val;
70 return 0;
71 }
72
73 return -EINVAL;
74 }
75
simple_strtol(const char * cp,char ** endp,unsigned int base)76 long simple_strtol(const char *cp, char **endp, unsigned int base)
77 {
78 if (*cp == '-')
79 return -simple_strtoul(cp + 1, endp, base);
80
81 return simple_strtoul(cp, endp, base);
82 }
83
dtb_node_read_bool(const struct dtb_node * node,const char * propname)84 rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname)
85 {
86 const void *prop;
87
88 RT_ASSERT(dtb_node_valid(node));
89 debug("%s: %s: ", __func__, propname);
90
91 prop = dtb_node_get_property(node, propname, NULL);
92
93 debug("%s\n", prop ? "true" : "false");
94
95 return prop ? RT_TRUE : RT_FALSE;
96 }
97
dtb_node_read_prop(const struct dtb_node * node,const char * propname,int * sizep)98 const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep)
99 {
100 const char *val = NULL;
101 int len;
102
103 RT_ASSERT(dtb_node_valid(node));
104 debug("%s: %s: ", __func__, propname);
105
106 struct dtb_property *prop = dtb_node_get_dtb_node_property(node, propname, &len);
107
108 if (prop)
109 {
110 val = prop->value;
111 len = prop->size;
112 }
113
114 if (!val)
115 {
116 debug("<not found>\n");
117 if (sizep)
118 *sizep = -FDT_ERR_NOTFOUND;
119 return NULL;
120 }
121 if (sizep)
122 *sizep = len;
123
124 return val;
125 }
126
dtb_node_read_string(const struct dtb_node * node,const char * propname)127 const char *dtb_node_read_string(const struct dtb_node *node, const char *propname)
128 {
129 const char *str;
130 int len;
131
132 str = dtb_node_read_prop(node, propname, &len);
133 if (!str)
134 return NULL;
135
136 if (strnlen(str, len) >= len)
137 {
138 debug("<invalid>\n");
139 return NULL;
140 }
141 debug("%s\n", str);
142
143 return str;
144 }
145
dtb_node_find_subnode(const struct dtb_node * node,const char * subnode_name)146 const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name)
147 {
148 const struct dtb_node *subnode;
149
150 RT_ASSERT(dtb_node_valid(node));
151 debug("%s: %s: ", __func__, subnode_name);
152
153 for (node = node->child; node; node = node->sibling)
154 {
155 if (!strcmp(subnode_name, node->name))
156 break;
157 }
158 subnode = node;
159
160 debug("%s\n", dtb_node_valid(subnode) ?\
161 dtb_node_get_name(subnode) : "<none>");
162
163 return subnode;
164 }
165
dtb_node_first_subnode(const struct dtb_node * node)166 struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node)
167 {
168 RT_ASSERT(dtb_node_valid(node));
169
170 return node->child;
171 }
172
dtb_node_next_subnode(const struct dtb_node * node)173 struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node)
174 {
175 RT_ASSERT(dtb_node_valid(node));
176
177 return node->sibling;
178 }
179
dtb_node_get_name(const struct dtb_node * node)180 const char *dtb_node_get_name(const struct dtb_node *node)
181 {
182 if (!dtb_node_valid(node))
183 {
184 debug("%s node not valid\n", __func__);
185 return NULL;
186 }
187
188 return strrchr(node->path, '/') + 1;
189
190 }
191
dtb_node_get_by_phandle(uint32_t phandle)192 struct dtb_node *dtb_node_get_by_phandle(uint32_t phandle)
193 {
194 if (dtb_node_active())
195 return dtb_node_find_node_by_phandle(phandle);
196 return NULL;
197 }
198
dtb_node_read_size(const struct dtb_node * node,const char * propname)199 int dtb_node_read_size(const struct dtb_node *node, const char *propname)
200 {
201 struct dtb_property *prop = dtb_node_get_dtb_node_property( node, propname, NULL);
202
203 if (prop)
204 return prop->size;
205
206 return -EINVAL;
207 }
208
dtb_node_get_addr_and_size_by_index(const struct dtb_node * node,int index,size_t * addr,size_t * size)209 int dtb_node_get_addr_and_size_by_index(const struct dtb_node *node, int index, size_t *addr, size_t *size)
210 {
211 const uint32_t *prop;
212 int psize;
213 int onesize, na, ns;
214
215 na = dtb_node_n_addr_cells(node);
216 ns = dtb_node_n_size_cells(node);
217
218 prop = dtb_node_get_dtb_node_property_value(node, "reg", &psize);
219 if (prop == NULL)
220 {
221 return -1;
222 }
223
224 psize /= 4;
225 onesize = na + ns;
226
227 if (psize >= (index + 1) * onesize)
228 {
229 prop += index * onesize;
230
231 if (addr)
232 {
233 *addr = dtb_node_read_number(prop, na);
234 }
235 if (size)
236 {
237 *size = dtb_node_read_number(prop + na, ns);
238 }
239
240 return 0;
241 }
242
243 return -1;
244 }
245
dtb_node_get_addr_index(const struct dtb_node * node,int index)246 size_t dtb_node_get_addr_index(const struct dtb_node *node, int index)
247 {
248 int na;
249 size_t size;
250
251 const uint32_t *prop_val;
252 uint flags;
253
254 prop_val = dtb_node_get_address(node, index,
255 (uint64_t *)&size, &flags);
256 if (!prop_val)
257 return -1;
258
259 na = dtb_node_n_addr_cells(node);
260
261 return dtb_node_read_number(prop_val, na);
262 }
263
dtb_node_get_addr(const struct dtb_node * node)264 size_t dtb_node_get_addr(const struct dtb_node *node)
265 {
266 return dtb_node_get_addr_index(node, 0);
267 }
268
dtb_node_stringlist_search(const struct dtb_node * node,const char * property,const char * string)269 int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
270 const char *string)
271 {
272 return dtb_node_property_match_string(node, property, string);
273 }
274
dtb_node_read_string_index(const struct dtb_node * node,const char * property,int index,const char ** outp)275 int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
276 const char **outp)
277 {
278 return dtb_node_property_read_string_index(node, property, index, outp);
279 }
280
dtb_node_read_string_count(const struct dtb_node * node,const char * property)281 int dtb_node_read_string_count(const struct dtb_node *node, const char *property)
282 {
283 return dtb_node_property_count_strings(node, property);
284 }
285
dtb_node_path(const char * path)286 struct dtb_node *dtb_node_path(const char *path)
287 {
288 if (dtb_node_active())
289 return dtb_node_find_node_by_path(path);
290 return NULL;
291 }
292
dtb_node_get_chosen_prop(const char * name)293 const char *dtb_node_get_chosen_prop(const char *name)
294 {
295 const struct dtb_node *chosen_node;
296
297 chosen_node = (const struct dtb_node *)dtb_node_path("/chosen");
298
299 return dtb_node_read_string(chosen_node, name);
300 }
301
dtb_node_get_chosen_node(const char * name)302 struct dtb_node *dtb_node_get_chosen_node(const char *name)
303 {
304 const char *prop;
305
306 prop = dtb_node_get_chosen_prop(name);
307 if (!prop)
308 return NULL;
309
310 return dtb_node_path(prop);
311 }
312
dtb_node_get_property(const struct dtb_node * node,const char * propname,int * lenp)313 const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp)
314 {
315 return dtb_node_get_dtb_node_property_value(node, propname, lenp);
316 }
317
dtb_node_is_available(const struct dtb_node * node)318 rt_bool_t dtb_node_is_available(const struct dtb_node *node)
319 {
320 return dtb_node_device_is_available(node);
321 }
322
dtb_node_get_addr_size(const struct dtb_node * node,const char * property,size_t * sizep)323 size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
324 size_t *sizep)
325 {
326 int na, ns;
327 int psize;
328 const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, property, &psize);
329
330 if (!prop)
331 return -1;
332 na = dtb_node_n_addr_cells(node);
333 ns = dtb_node_n_size_cells(node);
334 *sizep = dtb_node_read_number(prop + na, ns);
335
336 return dtb_node_read_number(prop, na);
337 }
338
dtb_node_read_u8_array_ptr(const struct dtb_node * node,const char * propname,size_t sz)339 const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname,
340 size_t sz)
341 {
342 int psize;
343 const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, propname, &psize);
344
345 if (!prop || sz != psize)
346 return NULL;
347 return (uint8_t *)prop;
348 }
349
dtb_node_find_all_compatible_node(const struct dtb_node * from,const char * compatible,struct dtb_node ** node_table,int max_num,int * node_num)350 int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num)
351 {
352 const struct dtb_node *dn;
353 int num = 0;
354 for_each_of_allnodes_from(from, dn)
355 {
356 if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
357 dtb_node_get(dn))
358 {
359
360 num++;
361 *node_table = (struct dtb_node *)dn;
362 node_table++;
363 if (num >= max_num)
364 {
365 break;
366 }
367 }
368 }
369 *node_num = num;
370 dtb_node_put(from);
371
372 return 0;
373 }
374
dtb_node_write_prop(const struct dtb_node * node,const char * propname,int len,const void * value)375 int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
376 const void *value)
377 {
378 struct dtb_property *pp;
379 struct dtb_property *pp_last = NULL;
380 struct dtb_property *new;
381
382 if (!dtb_node_active())
383 return -ENOSYS;
384
385 if (!node)
386 return -EINVAL;
387
388 for (pp = node->properties; pp; pp = pp->next)
389 {
390 if (strcmp(pp->name, propname) == 0)
391 {
392 /* Property exists -> change value */
393 pp->value = (void *)value;
394 pp->size = len;
395 return 0;
396 }
397 pp_last = pp;
398 }
399
400 if (!pp_last)
401 return -ENOENT;
402
403 /* Property does not exist -> append new property */
404 new = (struct dtb_property *)malloc(sizeof(struct dtb_property));
405 if (!new)
406 return -ENOMEM;
407
408 new->name = strdup(propname);
409 if (!new->name)
410 {
411 free(new);
412 return -ENOMEM;
413 }
414
415 new->value = (void *)value;
416 new->size = len;
417 new->next = NULL;
418
419 pp_last->next = new;
420
421 return 0;
422 }
423
dtb_node_write_string(const struct dtb_node * node,const char * propname,const char * value)424 int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value)
425 {
426 if (!dtb_node_active())
427 return -ENOSYS;
428
429 RT_ASSERT(dtb_node_valid(node));
430
431 debug("%s: %s = %s", __func__, propname, value);
432
433 return dtb_node_write_prop(node, propname, strlen(value) + 1, value);
434 }
435
dtb_node_set_enabled(const struct dtb_node * node,rt_bool_t value)436 int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value)
437 {
438 if (!dtb_node_active())
439 return -ENOSYS;
440
441 RT_ASSERT(dtb_node_valid(node));
442
443 if (value)
444 return dtb_node_write_string(node, "status", "okay");
445 else
446 return dtb_node_write_string(node, "status", "disable");
447 }
448
449 /**
450 * dtb_node_irq_find_parent - Given a device node, find its interrupt parent node
451 * @child: pointer to device node
452 *
453 * Returns a pointer to the interrupt parent node, or NULL if the interrupt
454 * parent could not be determined.
455 */
dtb_node_irq_find_parent(struct dtb_node * child)456 static struct dtb_node *dtb_node_irq_find_parent(struct dtb_node *child)
457 {
458 struct dtb_node *p;
459 phandle parent;
460
461 if (!dtb_node_get(child))
462 return NULL;
463 do
464 {
465 if (dtb_node_read_u32_array(child, "interrupt-parent", &parent, 1))
466 {
467 p = dtb_node_get_parent(child);
468 }
469 else
470 {
471 p = dtb_node_get_by_phandle(parent);
472 }
473 dtb_node_put(child);
474 child = p;
475 } while (p && dtb_node_get_property(p, "#interrupt-cells", NULL) == NULL);
476
477 return p;
478 }
479
dtb_node_irq_get(struct dtb_node * dev,int index)480 int dtb_node_irq_get(struct dtb_node *dev, int index)
481 {
482 int rc = 0;
483 struct fdt_phandle_args out_irq;
484 struct dtb_node *p;
485 uint32_t intsize;
486 int res, i;
487
488 p = dtb_node_irq_find_parent(dev);
489 if (p == NULL)
490 return -EINVAL;
491 /* Get size of interrupt specifier */
492 if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
493 {
494 res = -EINVAL;
495 goto out;
496 }
497
498 debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
499
500 /* Copy intspec into irq structure */
501 out_irq.np = p;
502 out_irq.args_count = intsize;
503 for (i = 0; i < intsize; i++)
504 {
505 res = dtb_node_read_u32_index(dev, "interrupts",
506 (index * 3 + i),
507 out_irq.args + i);
508 if (res)
509 goto out;
510 }
511 rc = out_irq.args[1];
512 out:
513 dtb_node_put(p);
514 return rc;
515
516 }
517
518 /**
519 * dtb_node_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
520 * @dev: pointer to device tree node
521 * @name: IRQ name
522 *
523 * Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
524 * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
525 * of any other failure.
526 */
dtb_node_irq_get_byname(struct dtb_node * dev,const char * name)527 int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name)
528 {
529 int index;
530
531 if (!name)
532 return -EINVAL;
533
534 index = dtb_node_stringlist_search(dev, "interrupt-names", name);
535 if (index < 0)
536 return index;
537
538 return dtb_node_irq_get(dev, index);
539 }
540
541 /**
542 * dtb_node_irq_count - Count the number of IRQs a node uses
543 * @dev: pointer to device tree node
544 */
dtb_node_irq_count(struct dtb_node * device)545 int dtb_node_irq_count(struct dtb_node *device)
546 {
547 struct dtb_node *p;
548 uint32_t intsize;
549 int nr = 0, res = 0;
550
551 p = dtb_node_irq_find_parent(device);
552 if (p == NULL)
553 return -EINVAL;
554
555 /* Get size of interrupt specifier */
556 if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
557 {
558 res = -EINVAL;
559 goto out;
560 }
561
562 debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
563
564 res = dtb_node_read_size(device, "interrupts");
565 if (res < 0)
566 {
567 goto out;
568 }
569 nr = res / (intsize * 4);
570 out:
571 dtb_node_put(p);
572
573 return nr;
574 }
575