// SPDX-License-Identifier: BSD-2-Clause /* * fdt_helper.c - Flat Device Tree manipulation helper routines * Implement helper routines on top of libfdt for OpenSBI usage * * Copyright (C) 2020 Bin Meng */ #include #include #include #include #include #include static int fdt_get_node_addr_size(void *fdt, int node, unsigned long *addr, unsigned long *size) { int parent, len, i; int cell_addr, cell_size; const fdt32_t *prop_addr, *prop_size; uint64_t temp = 0; parent = fdt_parent_offset(fdt, node); if (parent < 0) return parent; cell_addr = fdt_address_cells(fdt, parent); if (cell_addr < 1) return SBI_ENODEV; cell_size = fdt_size_cells(fdt, parent); if (cell_size < 0) return SBI_ENODEV; prop_addr = fdt_getprop(fdt, node, "reg", &len); if (!prop_addr) return SBI_ENODEV; prop_size = prop_addr + cell_addr; if (addr) { for (i = 0; i < cell_addr; i++) temp = (temp << 32) | fdt32_to_cpu(*prop_addr++); *addr = temp; } temp = 0; if (size) { for (i = 0; i < cell_size; i++) temp = (temp << 32) | fdt32_to_cpu(*prop_size++); *size = temp; } return 0; } int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart, const char *compatible) { int nodeoffset, len, rc; fdt32_t *val; unsigned long reg_addr, reg_size; /** * TODO: We don't know how to handle multiple nodes with the same * compatible sring. Just return the first node for now. */ nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); if (nodeoffset < 0) return nodeoffset; rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size); if (rc < 0 || !reg_addr || !reg_size) return SBI_ENODEV; uart->addr = reg_addr; /** * UART address is mandaotry. clock-frequency and current-speed may not * be present. Don't return error. */ val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len); if (len > 0 && val) uart->freq = fdt32_to_cpu(*val); val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len); if (len > 0 && val) uart->baud = fdt32_to_cpu(*val); return 0; } int fdt_parse_plic(void *fdt, struct platform_plic_data *plic, const char *compatible) { int nodeoffset, len, rc; const fdt32_t *val; unsigned long reg_addr, reg_size; nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); if (nodeoffset < 0) return nodeoffset; rc = fdt_get_node_addr_size(fdt, nodeoffset, ®_addr, ®_size); if (rc < 0 || !reg_addr || !reg_size) return SBI_ENODEV; plic->addr = reg_addr; val = fdt_getprop(fdt, nodeoffset, "riscv,ndev", &len); if (len > 0) plic->num_src = fdt32_to_cpu(*val); return 0; } int fdt_parse_clint(void *fdt, unsigned long *clint_addr, const char *compatible) { int nodeoffset, rc; nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); if (nodeoffset < 0) return nodeoffset; rc = fdt_get_node_addr_size(fdt, nodeoffset, clint_addr, NULL); if (rc < 0 || !clint_addr) return SBI_ENODEV; return 0; }