123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- #include "dtb_node.h"
- #include "libfdt.h"
- #include "libfdt_env.h"
- #include <ctype.h>
- static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
- {
- if (*base == 0)
- {
- if (s[0] == '0')
- {
- if (tolower(s[1]) == 'x' && isxdigit((int)s[2]))
- *base = 16;
- else
- *base = 8;
- }
- else
- *base = 10;
- }
- if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x')
- s += 2;
- return s;
- }
- unsigned long simple_strtoul(const char *cp, char **endp,
- unsigned int base)
- {
- unsigned long result = 0;
- unsigned long value;
- cp = _parse_integer_fixup_radix(cp, &base);
- while (isxdigit((int)*cp) && (value = isdigit((int)*cp) ? *cp-'0' : (islower((int)*cp)
- ? toupper(*cp) : *cp)-'A'+10) < base)
- {
- result = result*base + value;
- cp++;
- }
- if (endp)
- *endp = (char *)cp;
- return result;
- }
- int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
- {
- char *tail;
- unsigned long val;
- size_t len;
- *res = 0;
- len = strlen(cp);
- if (len == 0)
- return -EINVAL;
- val = simple_strtoul(cp, &tail, base);
- if (tail == cp)
- return -EINVAL;
- if ((*tail == '\0') ||
- ((len == (size_t)(tail - cp) + 1) && (*tail == '\n')))
- {
- *res = val;
- return 0;
- }
- return -EINVAL;
- }
- long simple_strtol(const char *cp, char **endp, unsigned int base)
- {
- if (*cp == '-')
- return -simple_strtoul(cp + 1, endp, base);
- return simple_strtoul(cp, endp, base);
- }
- rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname)
- {
- const void *prop;
- RT_ASSERT(dtb_node_valid(node));
- debug("%s: %s: ", __func__, propname);
- prop = dtb_node_get_property(node, propname, NULL);
- debug("%s\n", prop ? "true" : "false");
- return prop ? RT_TRUE : RT_FALSE;
- }
- const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep)
- {
- const char *val = NULL;
- int len;
- RT_ASSERT(dtb_node_valid(node));
- debug("%s: %s: ", __func__, propname);
- struct dtb_property *prop = dtb_node_get_dtb_node_property(node, propname, &len);
- if (prop)
- {
- val = prop->value;
- len = prop->size;
- }
- if (!val)
- {
- debug("<not found>\n");
- if (sizep)
- *sizep = -FDT_ERR_NOTFOUND;
- return NULL;
- }
- if (sizep)
- *sizep = len;
- return val;
- }
- const char *dtb_node_read_string(const struct dtb_node *node, const char *propname)
- {
- const char *str;
- int len;
- str = dtb_node_read_prop(node, propname, &len);
- if (!str)
- return NULL;
- if (strnlen(str, len) >= len)
- {
- debug("<invalid>\n");
- return NULL;
- }
- debug("%s\n", str);
- return str;
- }
- const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name)
- {
- const struct dtb_node *subnode;
- RT_ASSERT(dtb_node_valid(node));
- debug("%s: %s: ", __func__, subnode_name);
- for (node = node->child; node; node = node->sibling)
- {
- if (!strcmp(subnode_name, node->name))
- break;
- }
- subnode = node;
- debug("%s\n", dtb_node_valid(subnode) ?\
- dtb_node_get_name(subnode) : "<none>");
- return subnode;
- }
- struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node)
- {
- RT_ASSERT(dtb_node_valid(node));
- return node->child;
- }
- struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node)
- {
- RT_ASSERT(dtb_node_valid(node));
- return node->sibling;
- }
- const char *dtb_node_get_name(const struct dtb_node *node)
- {
- if (!dtb_node_valid(node))
- {
- debug("%s node not valid\n", __func__);
- return NULL;
- }
- return strrchr(node->path, '/') + 1;
- }
- struct dtb_node *dtb_node_get_by_phandle(uint32_t phandle)
- {
- if (dtb_node_active())
- return dtb_node_find_node_by_phandle(phandle);
- return NULL;
- }
- int dtb_node_read_size(const struct dtb_node *node, const char *propname)
- {
- struct dtb_property *prop = dtb_node_get_dtb_node_property( node, propname, NULL);
- if (prop)
- return prop->size;
- return -EINVAL;
- }
- int dtb_node_get_addr_and_size_by_index(const struct dtb_node *node, int index, size_t *addr, size_t *size)
- {
- const uint32_t *prop;
- int psize;
- int onesize, na, ns;
- na = dtb_node_n_addr_cells(node);
- ns = dtb_node_n_size_cells(node);
- prop = dtb_node_get_dtb_node_property_value(node, "reg", &psize);
- if (prop == NULL)
- {
- return -1;
- }
- psize /= 4;
- onesize = na + ns;
- if (psize >= (index + 1) * onesize)
- {
- prop += index * onesize;
- if (addr)
- {
- *addr = dtb_node_read_number(prop, na);
- }
- if (size)
- {
- *size = dtb_node_read_number(prop + na, ns);
- }
- return 0;
- }
- return -1;
- }
- size_t dtb_node_get_addr_index(const struct dtb_node *node, int index)
- {
- int na;
- size_t size;
- const uint32_t *prop_val;
- uint flags;
- prop_val = dtb_node_get_address(node, index,
- (uint64_t *)&size, &flags);
- if (!prop_val)
- return -1;
- na = dtb_node_n_addr_cells(node);
- return dtb_node_read_number(prop_val, na);
- }
- size_t dtb_node_get_addr(const struct dtb_node *node)
- {
- return dtb_node_get_addr_index(node, 0);
- }
- int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
- const char *string)
- {
- return dtb_node_property_match_string(node, property, string);
- }
- int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
- const char **outp)
- {
- return dtb_node_property_read_string_index(node, property, index, outp);
- }
- int dtb_node_read_string_count(const struct dtb_node *node, const char *property)
- {
- return dtb_node_property_count_strings(node, property);
- }
- struct dtb_node *dtb_node_path(const char *path)
- {
- if (dtb_node_active())
- return dtb_node_find_node_by_path(path);
- return NULL;
- }
- const char *dtb_node_get_chosen_prop(const char *name)
- {
- const struct dtb_node *chosen_node;
- chosen_node = (const struct dtb_node *)dtb_node_path("/chosen");
- return dtb_node_read_string(chosen_node, name);
- }
- struct dtb_node *dtb_node_get_chosen_node(const char *name)
- {
- const char *prop;
- prop = dtb_node_get_chosen_prop(name);
- if (!prop)
- return NULL;
- return dtb_node_path(prop);
- }
- const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp)
- {
- return dtb_node_get_dtb_node_property_value(node, propname, lenp);
- }
- rt_bool_t dtb_node_is_available(const struct dtb_node *node)
- {
- return dtb_node_device_is_available(node);
- }
- size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
- size_t *sizep)
- {
- int na, ns;
- int psize;
- const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, property, &psize);
- if (!prop)
- return -1;
- na = dtb_node_n_addr_cells(node);
- ns = dtb_node_n_size_cells(node);
- *sizep = dtb_node_read_number(prop + na, ns);
- return dtb_node_read_number(prop, na);
- }
- const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname,
- size_t sz)
- {
- int psize;
- const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, propname, &psize);
- if (!prop || sz != psize)
- return NULL;
- return (uint8_t *)prop;
- }
- 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)
- {
- const struct dtb_node *dn;
- int num = 0;
- for_each_of_allnodes_from(from, dn)
- {
- if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
- dtb_node_get(dn))
- {
- num++;
- *node_table = (struct dtb_node *)dn;
- node_table++;
- if (num >= max_num)
- {
- break;
- }
- }
- }
- *node_num = num;
- dtb_node_put(from);
- return 0;
- }
- int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
- const void *value)
- {
- struct dtb_property *pp;
- struct dtb_property *pp_last = NULL;
- struct dtb_property *new;
- if (!dtb_node_active())
- return -ENOSYS;
- if (!node)
- return -EINVAL;
- for (pp = node->properties; pp; pp = pp->next)
- {
- if (strcmp(pp->name, propname) == 0)
- {
- /* Property exists -> change value */
- pp->value = (void *)value;
- pp->size = len;
- return 0;
- }
- pp_last = pp;
- }
- if (!pp_last)
- return -ENOENT;
- /* Property does not exist -> append new property */
- new = (struct dtb_property *)malloc(sizeof(struct dtb_property));
- if (!new)
- return -ENOMEM;
- new->name = strdup(propname);
- if (!new->name)
- {
- free(new);
- return -ENOMEM;
- }
- new->value = (void *)value;
- new->size = len;
- new->next = NULL;
- pp_last->next = new;
- return 0;
- }
- int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value)
- {
- if (!dtb_node_active())
- return -ENOSYS;
- RT_ASSERT(dtb_node_valid(node));
- debug("%s: %s = %s", __func__, propname, value);
- return dtb_node_write_prop(node, propname, strlen(value) + 1, value);
- }
- int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value)
- {
- if (!dtb_node_active())
- return -ENOSYS;
- RT_ASSERT(dtb_node_valid(node));
- if (value)
- return dtb_node_write_string(node, "status", "okay");
- else
- return dtb_node_write_string(node, "status", "disable");
- }
- /**
- * dtb_node_irq_find_parent - Given a device node, find its interrupt parent node
- * @child: pointer to device node
- *
- * Returns a pointer to the interrupt parent node, or NULL if the interrupt
- * parent could not be determined.
- */
- static struct dtb_node *dtb_node_irq_find_parent(struct dtb_node *child)
- {
- struct dtb_node *p;
- phandle parent;
- if (!dtb_node_get(child))
- return NULL;
- do
- {
- if (dtb_node_read_u32_array(child, "interrupt-parent", &parent, 1))
- {
- p = dtb_node_get_parent(child);
- }
- else
- {
- p = dtb_node_get_by_phandle(parent);
- }
- dtb_node_put(child);
- child = p;
- } while (p && dtb_node_get_property(p, "#interrupt-cells", NULL) == NULL);
- return p;
- }
- int dtb_node_irq_get(struct dtb_node *dev, int index)
- {
- int rc = 0;
- struct fdt_phandle_args out_irq;
- struct dtb_node *p;
- uint32_t intsize;
- int res, i;
- p = dtb_node_irq_find_parent(dev);
- if (p == NULL)
- return -EINVAL;
- /* Get size of interrupt specifier */
- if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
- {
- res = -EINVAL;
- goto out;
- }
- debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
- /* Copy intspec into irq structure */
- out_irq.np = p;
- out_irq.args_count = intsize;
- for (i = 0; i < intsize; i++)
- {
- res = dtb_node_read_u32_index(dev, "interrupts",
- (index * 3 + i),
- out_irq.args + i);
- if (res)
- goto out;
- }
- rc = out_irq.args[1];
- out:
- dtb_node_put(p);
- return rc;
- }
- /**
- * dtb_node_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
- * @dev: pointer to device tree node
- * @name: IRQ name
- *
- * Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
- * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
- * of any other failure.
- */
- int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name)
- {
- int index;
- if (!name)
- return -EINVAL;
- index = dtb_node_stringlist_search(dev, "interrupt-names", name);
- if (index < 0)
- return index;
- return dtb_node_irq_get(dev, index);
- }
- /**
- * dtb_node_irq_count - Count the number of IRQs a node uses
- * @dev: pointer to device tree node
- */
- int dtb_node_irq_count(struct dtb_node *device)
- {
- struct dtb_node *p;
- uint32_t intsize;
- int nr = 0, res = 0;
- p = dtb_node_irq_find_parent(device);
- if (p == NULL)
- return -EINVAL;
- /* Get size of interrupt specifier */
- if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
- {
- res = -EINVAL;
- goto out;
- }
- debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
- res = dtb_node_read_size(device, "interrupts");
- if (res < 0)
- {
- goto out;
- }
- nr = res / (intsize * 4);
- out:
- dtb_node_put(p);
- return nr;
- }
|