summaryrefslogtreecommitdiffstats
path: root/bsps/shared/ofw
diff options
context:
space:
mode:
authorG S Niteesh Babu <niteesh.gs@gmail.com>2020-12-04 13:40:46 +0530
committerChristian Mauderer <oss@c-mauderer.de>2020-12-27 10:05:02 +0100
commit9d2ed41fcb1dc635ce7d689c60cdf374f2394dbd (patch)
tree35ab866a4f77dd1caf8f627fffa812f0a7d53889 /bsps/shared/ofw
parentUpdate header.am (diff)
downloadrtems-9d2ed41fcb1dc635ce7d689c60cdf374f2394dbd.tar.bz2
bsps/shared/ofw: Implement RTEMS OFW interface
RTEMS OFW is a FDT only implementation of the OpenFirmWare interface. This API is created to be compatible with FreeBSD OpenFirmWare interface. The main intention is to make porting of FreeBSD drivers to RTEMS easier. Most functions implemented have an direct one-one mapping with the original OFW API and some extra auxiliary functions were implemented to make working with device trees easier in RTEMS. Update #3784
Diffstat (limited to 'bsps/shared/ofw')
-rw-r--r--bsps/shared/ofw/ofw.c683
1 files changed, 683 insertions, 0 deletions
diff --git a/bsps/shared/ofw/ofw.c b/bsps/shared/ofw/ofw.c
new file mode 100644
index 0000000000..82924b2600
--- /dev/null
+++ b/bsps/shared/ofw/ofw.c
@@ -0,0 +1,683 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup ofw
+ */
+
+/*
+ * Copyright (C) 2020 Niteesh Babu G S <niteesh.gs@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <bsp/fdt.h>
+#include <sys/param.h>
+#include <ofw/ofw.h>
+#include <libfdt.h>
+#include <assert.h>
+#include <rtems/sysinit.h>
+#include <ofw/ofw_test.h>
+
+static void *fdtp = NULL;
+
+static phandle_t rtems_fdt_offset_to_phandle( int offset )
+{
+ if (offset < 0) {
+ return 0;
+ }
+
+ return (phandle_t)offset + fdt_off_dt_struct(fdtp);
+}
+
+static int rtems_fdt_phandle_to_offset( phandle_t handle )
+{
+ int off;
+ int fdt_off;
+
+ off = (int) handle;
+ fdt_off = fdt_off_dt_struct(fdtp);
+
+ if (off < fdt_off) {
+ return -1;
+
+ }
+
+ return off - fdt_off;
+}
+
+void rtems_ofw_init( void ) {
+ int rv;
+ const void *fdt;
+
+ fdt = bsp_fdt_get();
+
+ rv = fdt_check_header(fdt);
+
+ /*
+ * If the FDT is invalid exit through fatal.
+ */
+ if (rv != 0) {
+ rtems_fatal_error_occurred(RTEMS_NOT_CONFIGURED);
+ }
+
+ fdtp = (void *)fdt;
+}
+
+RTEMS_SYSINIT_ITEM(
+ rtems_ofw_init,
+ RTEMS_SYSINIT_BSP_PRE_DRIVERS,
+ RTEMS_SYSINIT_ORDER_FIRST
+);
+
+phandle_t rtems_ofw_peer( phandle_t node )
+{
+ int offset;
+
+ if (node == 0) {
+ int root = fdt_path_offset(fdtp, "/");
+ return rtems_fdt_offset_to_phandle(root);
+ }
+
+ offset = rtems_fdt_phandle_to_offset(node);
+ if (offset < 0) {
+ return 0;
+ }
+
+ offset = fdt_next_subnode(fdtp, offset);
+ return rtems_fdt_offset_to_phandle(offset);
+}
+
+phandle_t rtems_ofw_child( phandle_t node )
+{
+ int offset;
+
+ offset = rtems_fdt_phandle_to_offset(node);
+
+ if (offset < 0) {
+ return 0;
+ }
+
+ offset = fdt_first_subnode(fdtp, offset);
+ return rtems_fdt_offset_to_phandle(offset);
+}
+
+phandle_t rtems_ofw_parent( phandle_t node )
+{
+ int offset;
+
+ offset = rtems_fdt_phandle_to_offset(node);
+
+ if (offset < 0) {
+ return 0;
+ }
+
+ offset = fdt_parent_offset(fdtp, offset);
+ return rtems_fdt_offset_to_phandle(offset);
+}
+
+ssize_t rtems_ofw_get_prop_len(
+ phandle_t node,
+ const char *propname
+)
+{
+ int offset;
+ int len;
+ const void *prop;
+
+ offset = rtems_fdt_phandle_to_offset(node);
+
+ if (offset < 0) {
+ return -1;
+ }
+
+ prop = fdt_getprop(fdtp, offset, propname, &len);
+
+ if (prop == NULL && strcmp(propname, "name") == 0) {
+ fdt_get_name(fdtp, offset, &len);
+ return len + 1;
+ }
+
+ if (prop == NULL && strcmp(propname, "/chosen") == 0) {
+ if (strcmp(propname, "fdtbootcpu") == 0)
+ return sizeof(pcell_t);
+ if (strcmp(propname, "fdtmemreserv") == 0)
+ return 2 * sizeof(uint64_t) * fdt_num_mem_rsv(fdtp);
+ }
+
+ if (prop == NULL) {
+ return -1;
+ }
+
+ return len;
+}
+
+ssize_t rtems_ofw_get_prop(
+ phandle_t node,
+ const char *propname,
+ void *buf,
+ size_t bufsize
+)
+{
+ const void *prop;
+ int offset;
+ int len;
+ uint32_t cpuid;
+
+ offset = rtems_fdt_phandle_to_offset(node);
+
+ if (offset < 0) {
+ return -1;
+ }
+
+ prop = fdt_getprop(fdtp, offset, propname, &len);
+
+ if (prop == NULL && strcmp(propname, "name") == 0) {
+ prop = fdt_get_name(fdtp, offset, &len);
+ strncpy(buf, prop, bufsize);
+ return len + 1;
+ }
+
+ if (prop == NULL && strcmp(propname, "/chosen") == 0) {
+ if (strcmp(propname, "fdtbootcpu") == 0) {
+ cpuid = cpu_to_fdt32(fdt_boot_cpuid_phys(fdtp));
+ len = sizeof(cpuid);
+ prop = &cpuid;
+ }
+ if (strcmp(propname, "fdtmemreserv") == 0) {
+ prop = (char *)fdtp + fdt_off_mem_rsvmap(fdtp);
+ len = sizeof(uint64_t)*2*fdt_num_mem_rsv(fdtp);
+ }
+ }
+
+ if (prop == NULL) {
+ return -1;
+ }
+
+ bcopy(prop, buf, MIN(len, bufsize));
+
+ return len;
+}
+
+ssize_t rtems_ofw_get_enc_prop(
+ phandle_t node,
+ const char *prop,
+ pcell_t *buf,
+ size_t len
+)
+{
+ ssize_t rv;
+
+ assert(len % 4 == 0);
+ rv = rtems_ofw_get_prop(node, prop, buf, len);
+
+ if (rv < 0) {
+ return rv;
+ }
+
+ for (int i = 0; i < (len / 4); i++) {
+ buf[i] = fdt32_to_cpu(buf[i]);
+ }
+
+ return rv;
+}
+
+int rtems_ofw_has_prop(
+ phandle_t node,
+ const char *propname
+)
+{
+ ssize_t rv;
+
+ rv = rtems_ofw_get_prop_len(node, propname);
+ return rv >= 0 ? 1 : 0;
+}
+
+ssize_t rtems_ofw_search_prop(
+ phandle_t node,
+ const char *propname,
+ void *buf,
+ size_t len
+)
+{
+ ssize_t rv;
+
+ for (; node != 0; node = rtems_ofw_parent(node)) {
+ if ((rv = rtems_ofw_get_prop(node, propname, buf, len) != -1)) {
+ return rv;
+ }
+ }
+
+ return -1;
+}
+
+ssize_t rtems_ofw_search_enc_prop(
+ phandle_t node,
+ const char *propname,
+ pcell_t *buf,
+ size_t len
+)
+{
+ ssize_t rv;
+
+ for (; node != 0; node = rtems_ofw_parent(node)) {
+ if ((rv = rtems_ofw_get_enc_prop(node, propname, buf, len) != -1)) {
+ return rv;
+ }
+ }
+
+ return -1;
+}
+
+ssize_t rtems_ofw_get_prop_alloc(
+ phandle_t node,
+ const char *propname,
+ void **buf
+)
+{
+ ssize_t len;
+
+ *buf = NULL;
+ if ((len = rtems_ofw_get_prop_len(node, propname)) == -1) {
+ return -1;
+ }
+
+ if (len > 0) {
+ *buf = malloc(len);
+ if (*buf == NULL) {
+ return -1;
+ }
+
+ if (rtems_ofw_get_prop(node, propname, *buf, len) == -1) {
+ rtems_ofw_free(buf);
+ *buf = NULL;
+ return -1;
+ }
+ }
+
+ return len;
+}
+
+ssize_t rtems_ofw_get_prop_alloc_multi(
+ phandle_t node,
+ const char *propname,
+ int elsz,
+ void **buf
+)
+{
+ ssize_t len;
+
+ *buf = NULL;
+ if ((len = rtems_ofw_get_prop_len(node, propname)) == -1 ||
+ (len % elsz != 0)) {
+ return -1;
+ }
+
+ if (len > 0) {
+ *buf = malloc(len);
+ if (*buf == NULL) {
+ return -1;
+ }
+
+ if (rtems_ofw_get_prop(node, propname, *buf, len) == -1) {
+ rtems_ofw_free(buf);
+ *buf = NULL;
+ return -1;
+ }
+ }
+
+ return (len / elsz);
+}
+
+ssize_t rtems_ofw_get_enc_prop_alloc(
+ phandle_t node,
+ const char *propname,
+ void **buf
+)
+{
+ ssize_t len;
+
+ *buf = NULL;
+ if ((len = rtems_ofw_get_prop_len(node, propname)) == -1) {
+ return -1;
+ }
+
+ if (len > 0) {
+ *buf = malloc(len);
+ if (*buf == NULL) {
+ return -1;
+ }
+
+ if (rtems_ofw_get_enc_prop(node, propname, *buf, len) == -1) {
+ rtems_ofw_free(buf);
+ *buf = NULL;
+ return -1;
+ }
+ }
+
+ return len;
+}
+
+ssize_t rtems_ofw_get_enc_prop_alloc_multi(
+ phandle_t node,
+ const char *propname,
+ int elsz,
+ void **buf
+)
+{
+ ssize_t len;
+
+ *buf = NULL;
+ if ((len = rtems_ofw_get_prop_len(node, propname)) == -1 ||
+ (len % elsz != 0)) {
+ return -1;
+ }
+
+ if (len > 0) {
+ *buf = malloc(len);
+ if (*buf == NULL) {
+ return -1;
+ }
+
+ if (rtems_ofw_get_enc_prop(node, propname, *buf, len) == -1) {
+ rtems_ofw_free(buf);
+ *buf = NULL;
+ return -1;
+ }
+ }
+
+ return (len / elsz);
+}
+
+void rtems_ofw_free( void *buf )
+{
+ free(buf);
+}
+
+int rtems_ofw_next_prop(
+ phandle_t node,
+ const char *previous,
+ char *buf,
+ size_t len
+)
+{
+ const void *name;
+ const void *prop;
+ int offset;
+
+ offset = rtems_fdt_phandle_to_offset(node);
+
+ if (offset < 0) {
+ return -1;
+ }
+
+ if (previous == NULL) {
+ offset = fdt_first_property_offset(fdtp, offset);
+ } else {
+ fdt_for_each_property_offset(offset, fdtp, offset) {
+ prop = fdt_getprop_by_offset(fdtp, offset, (const char **)&name, NULL);
+ if (prop == NULL)
+ return -1;
+
+ if (strcmp(previous, name) != 0)
+ continue;
+
+ offset = fdt_next_property_offset(fdtp, offset);
+ break;
+ }
+ }
+
+ if (offset < 0)
+ return 0;
+
+ prop = fdt_getprop_by_offset(fdtp, offset, (const char **)&name, &offset);
+ if (prop == NULL)
+ return -1;
+
+ strncpy(buf, name, len);
+
+ return 1;
+}
+
+int rtems_ofw_set_prop(
+ phandle_t node,
+ const char *name,
+ const void *buf,
+ size_t len
+)
+{
+ int offset;
+
+ offset = rtems_fdt_phandle_to_offset(node);
+
+ if (offset < 0)
+ return -1;
+
+ if (fdt_setprop_inplace(fdtp, offset, name, buf, len) != 0)
+ return (fdt_setprop(fdtp, offset, name, buf, len));
+
+ return 0;
+}
+
+phandle_t rtems_ofw_find_device( const char *path )
+{
+ int offset;
+
+ offset = fdt_path_offset(fdtp, path);
+ if (offset < 0)
+ return -1;
+
+ return rtems_fdt_offset_to_phandle(offset);
+}
+
+static phandle_t rtems_ofw_get_effective_phandle(
+ phandle_t node,
+ phandle_t xref
+)
+{
+ phandle_t child;
+ phandle_t ref;
+
+ for (child = rtems_ofw_child(node); child != 0; child = rtems_ofw_peer(child)) {
+ ref = rtems_ofw_get_effective_phandle(child, xref);
+ if (ref != -1)
+ return ref;
+
+ if (rtems_ofw_get_enc_prop(child, "phandle", &ref, sizeof(ref)) == -1 &&
+ rtems_ofw_get_enc_prop(child, "ibm,phandle", &ref, sizeof(ref)) == -1 &&
+ rtems_ofw_get_enc_prop(child, "linux,phandle", &ref, sizeof(ref)) == -1
+ ) {
+ continue;
+ }
+
+ if (ref == xref)
+ return child;
+ }
+
+ return -1;
+}
+
+phandle_t rtems_ofw_node_from_xref( phandle_t xref )
+{
+ phandle_t node;
+
+ if ((node = rtems_ofw_get_effective_phandle(rtems_ofw_peer(0), xref)) == -1)
+ return xref;
+
+ return node;
+}
+
+phandle_t rtems_ofw_xref_from_node( phandle_t node )
+{
+ phandle_t ref;
+
+ if (rtems_ofw_get_enc_prop(node, "phandle", &ref, sizeof(ref)) == -1 &&
+ rtems_ofw_get_enc_prop(node, "ibm,phandle", &ref, sizeof(ref)) == -1 &&
+ rtems_ofw_get_enc_prop(node, "linux,phandle", &ref, sizeof(ref)) == -1)
+ {
+ return node;
+ }
+
+ return ref;
+}
+
+phandle_t rtems_ofw_instance_to_package( ihandle_t instance )
+{
+ return rtems_ofw_node_from_xref(instance);
+}
+
+ssize_t rtems_ofw_package_to_path(
+ phandle_t node,
+ char *buf,
+ size_t len
+)
+{
+ int offset;
+ int rv;
+
+ offset = rtems_fdt_phandle_to_offset(node);
+
+ rv = fdt_get_path(fdtp, offset, buf, len);
+ if (rv != 0)
+ return -1;
+
+ return rv;
+}
+
+ssize_t rtems_ofw_instance_to_path(
+ ihandle_t instance,
+ char *buf,
+ size_t len
+)
+{
+ int offset;
+ int rv;
+
+ offset = rtems_ofw_instance_to_package(instance);
+ offset = rtems_fdt_phandle_to_offset(offset);
+
+ rv = fdt_get_path(fdtp, offset, buf, len);
+ if (rv != 0)
+ return -1;
+
+ return rv;
+}
+
+int rtems_ofw_get_reg(
+ phandle_t node,
+ rtems_ofw_memory_area *buf,
+ size_t size
+)
+{
+ int len;
+ int offset;
+ int nranges;
+ int nregs;
+ phandle_t parent;
+ rtems_ofw_ranges range;
+ const rtems_ofw_ranges *ptr;
+
+ len = rtems_ofw_get_enc_prop(node, "reg", (pcell_t *)buf, size);
+ if (len <= 0) {
+ return len;
+ }
+
+ nregs = MIN(len, size) / sizeof(rtems_ofw_memory_area);
+
+ for (parent = rtems_ofw_parent(node); parent > 0;
+ parent = rtems_ofw_parent(parent)) {
+
+ offset = rtems_fdt_phandle_to_offset(parent);
+ ptr = fdt_getprop(fdtp, offset, "ranges", &len);
+
+ if (len < 0) {
+ break;
+ }
+
+ nranges = len / sizeof(rtems_ofw_ranges);
+
+ offset = 0;
+ for (int i=0; i < nregs; i++) {
+ for (int j=0; j < nranges; j++) {
+
+ range.parent_bus = fdt32_to_cpu(ptr[j].parent_bus);
+ range.child_bus = fdt32_to_cpu(ptr[j].child_bus);
+ range.size = fdt32_to_cpu(ptr[j].size);
+
+ if (buf[i].start >= range.child_bus &&
+ buf[i].start < range.child_bus + range.size) {
+ offset = range.parent_bus - range.child_bus;
+ break;
+ }
+
+ }
+ buf[i].start += offset;
+ }
+ }
+
+ return nregs;
+}
+
+int rtems_ofw_get_interrupts(
+ phandle_t node,
+ rtems_vector_number *buf,
+ size_t size
+)
+{
+ int rv;
+
+ rv = rtems_ofw_get_enc_prop(node, "interrupts", buf, size);
+
+ if (rv <= 0) {
+ return rv;
+ }
+
+ return MIN(size, rv) / sizeof(rtems_vector_number);
+}
+
+bool rtems_ofw_node_status( phandle_t node )
+{
+ int len;
+ const char buf[10];
+
+ len = rtems_ofw_get_prop(node, "status", (void *)&buf[0], sizeof(buf));
+ if ((len == -1) ||
+ (strncmp(buf, "okay", MIN(5, len)) == 0) ||
+ (strncmp(buf, "ok", MIN(3, len)) == 0)) {
+ return true;
+ }
+
+ return false;
+}
+
+phandle_t rtems_ofw_find_device_by_compat( const char *compat )
+{
+ int offset;
+
+ offset = fdt_node_offset_by_compatible(fdtp, -1, compat);
+ return rtems_fdt_offset_to_phandle(offset);
+}