/**************************************************************************** * drivers/devicetree/fdt.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include /**************************************************************************** * Private Data ****************************************************************************/ /* Location of the fdt data for this system. */ static FAR const char *g_fdt_base = NULL; /**************************************************************************** * Public Functions ****************************************************************************/ int fdt_register(FAR const char *fdt_base) { struct fdt_header_s *fdt_header; DEBUGASSERT(fdt_base); fdt_header = (struct fdt_header_s *)fdt_base; if (fdt_header->magic != be32toh(FDT_MAGIC)) { return -EINVAL; /* Bad magic byte read */ } g_fdt_base = fdt_base; return OK; } FAR const char *fdt_get(void) { return g_fdt_base; } int fdt_get_irq(FAR const void *fdt, int nodeoffset, int offset, int irqbase) { FAR const fdt32_t *pv; int irq = -1; pv = fdt_getprop(fdt, nodeoffset, "interrupts", NULL); if (pv != NULL) { irq = fdt32_ld(pv + offset) + irqbase; } return irq; } int fdt_get_irq_by_path(FAR const void *fdt, int offset, const char *path, int irqbase) { return fdt_get_irq(fdt, fdt_path_offset(fdt, path), offset, irqbase); } int fdt_get_parent_address_cells(FAR const void *fdt, int offset) { int parentoff; parentoff = fdt_parent_offset(fdt, offset); if (parentoff < 0) { return parentoff; } return fdt_address_cells(fdt, parentoff); } int fdt_get_parent_size_cells(FAR const void *fdt, int offset) { int parentoff; parentoff = fdt_parent_offset(fdt, offset); if (parentoff < 0) { return parentoff; } return fdt_size_cells(fdt, parentoff); } uintptr_t fdt_ld_by_cells(FAR const void *value, int cells) { if (cells == 2) { return fdt64_ld(value); } else { return fdt32_ld(value); } } uintptr_t fdt_get_reg_base_by_name(FAR const void *fdt, int offset, const char *reg_name) { uintptr_t addr = 0; int reg_index = fdt_stringlist_search(fdt, offset, "reg-names", reg_name); if (reg_index < 0) { return addr; } return fdt_get_reg_base(fdt, offset, reg_index); } uintptr_t fdt_get_reg_base(FAR const void *fdt, int offset, int index) { FAR const void *reg; uintptr_t addr = 0; int reg_length; /* Register cells contain a tuple of two values */ index *= 2; reg = fdt_getprop(fdt, offset, "reg", ®_length); if (reg != NULL) { if ((index * sizeof(uintptr_t)) > reg_length) { return addr; } addr = fdt_ld_by_cells(reg + index * sizeof(uintptr_t), fdt_get_parent_address_cells(fdt, offset)); } return addr; } uintptr_t fdt_get_reg_size(FAR const void *fdt, int offset) { FAR const void *reg; uintptr_t size = 0; reg = fdt_getprop(fdt, offset, "reg", NULL); if (reg != NULL) { size = fdt_ld_by_cells(reg, fdt_get_parent_size_cells(fdt, offset)); } return size; } uintptr_t fdt_get_reg_base_by_path(FAR const void *fdt, FAR const char *path) { return fdt_get_reg_base(fdt, fdt_path_offset(fdt, path), 0); } bool fdt_device_is_available(FAR const void *fdt, int node) { const char *status = fdt_getprop(fdt, node, "status", NULL); if (!status) { return true; } if (!strcmp(status, "ok") || !strcmp(status, "okay")) { return true; } return false; } const char *fdt_get_node_label(FAR const void *fdt, int node) { int symbols_offset; int property_offset; int ret; const char *property_name; const char *label_name; char path_buffer[CONFIG_PATH_MAX] = { 0 }; symbols_offset = fdt_path_offset(fdt, "/__symbols__"); if (symbols_offset < 0) { return NULL; } ret = fdt_get_path(fdt, node, path_buffer, sizeof(path_buffer)); if (ret < 0) { return NULL; } fdt_for_each_property_offset(property_offset, fdt, symbols_offset) { property_name = fdt_getprop_by_offset( fdt, property_offset, &label_name, NULL); /* The symbols section is a list of parameters in the format * label_name = node_path. So the value of each property needs to be * checked with the full path found earlier. * */ if (!strncmp(property_name, path_buffer, sizeof(path_buffer))) { return label_name; } } return NULL; } uintptr_t fdt_get_clock_frequency(FAR const void *fdt, int offset) { const void *pv; uintptr_t clock_frequency = 0; pv = fdt_getprop(fdt, offset, "clock-frequency", NULL); if (!pv) { return clock_frequency; } clock_frequency = fdt_ld_by_cells(pv, fdt_get_parent_address_cells(fdt, offset)); return clock_frequency; } uintptr_t fdt_get_clock_frequency_from_clocks(FAR const void *fdt, int offset, int index) { const fdt32_t *pv; fdt32_t clk_phandle; int pv_offset; uintptr_t clock_frequency = 0; int clk_length; pv = fdt_getprop(fdt, offset, "clocks", &clk_length); if (!pv) { return clock_frequency; } if ((index * sizeof(fdt32_t)) > clk_length) { return clock_frequency; } clk_phandle = fdt32_ld(pv + index); pv_offset = fdt_node_offset_by_phandle(fdt, clk_phandle); if (pv_offset < 0) { return clock_frequency; } return fdt_get_clock_frequency(fdt, pv_offset); } int fdt_node_index_from_label(FAR const char *node_label, int count) { int dev_number = 0; size_t label_length; if (!node_label) { return -ENOENT; } label_length = strnlen(node_label, CONFIG_PATH_MAX); if (count > label_length || count <= 0) { return -EINVAL; } for (int i = 0; i < count; i++) { int number = atoi(&node_label[label_length - i]); if (number) { dev_number = number; } } /* atoi returns 0 on failure, so check that the number isn't actually 0 */ if (!dev_number && !isdigit(node_label[label_length - 1])) { return -ENOENT; } return dev_number; } void fdt_node_from_compat(FAR const void *fdt, FAR const char **compatible_ids, FAR void (*driver_callback)(FAR const void *fdt, int offset)) { int offset = 0; DEBUGASSERT(compatible_ids); DEBUGASSERT(driver_callback); while (*compatible_ids) { while (true) { offset = fdt_node_offset_by_compatible(fdt, offset, *compatible_ids); if (offset == -FDT_ERR_NOTFOUND) { break; } if (!fdt_device_is_available(fdt, offset)) { continue; } driver_callback(fdt, offset); } compatible_ids++; } } int fdt_load_prop_u32(FAR const void *fdt, int offset, FAR const char *property, int index, FAR uint32_t *value) { DEBUGASSERT(property); DEBUGASSERT(value); int length; const fdt32_t *pv = fdt_getprop(fdt, offset, property, &length); if (!pv) { return -ENOENT; } if (index >= length) { return -EINVAL; } *value = fdt32_ld(pv + index); return OK; }