diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-03-02 16:28:14 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2017-03-08 09:00:03 +0100 |
commit | f0dd0c506a706542e1a6b1c3e78e6c9809d31085 (patch) | |
tree | 7b0707ca37cc697da33ee43deb267f6cd9f20d8a /freebsd/sys/dev/fdt | |
parent | media01: Add cpuinfo command (diff) | |
download | rtems-libbsd-f0dd0c506a706542e1a6b1c3e78e6c9809d31085.tar.bz2 |
FDT(4): Import from FreeBSD
Diffstat (limited to 'freebsd/sys/dev/fdt')
-rw-r--r-- | freebsd/sys/dev/fdt/fdt_common.c | 744 | ||||
-rw-r--r-- | freebsd/sys/dev/fdt/fdt_common.h | 107 | ||||
-rw-r--r-- | freebsd/sys/dev/fdt/simplebus.c | 431 | ||||
-rw-r--r-- | freebsd/sys/dev/fdt/simplebus.h | 64 |
4 files changed, 1346 insertions, 0 deletions
diff --git a/freebsd/sys/dev/fdt/fdt_common.c b/freebsd/sys/dev/fdt/fdt_common.c new file mode 100644 index 00000000..efbef9de --- /dev/null +++ b/freebsd/sys/dev/fdt/fdt_common.c @@ -0,0 +1,744 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2009-2014 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Andrew Turner under sponsorship from + * the FreeBSD Foundation. + * This software was developed by Semihalf under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHOR 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/limits.h> +#include <sys/sysctl.h> + +#include <machine/resource.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> + +#include <rtems/bsd/local/ofw_bus_if.h> + +#ifdef DEBUG +#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ + printf(fmt,##args); } while (0) +#else +#define debugf(fmt, args...) +#endif + +#define FDT_COMPAT_LEN 255 +#define FDT_TYPE_LEN 64 + +#define FDT_REG_CELLS 4 + +SYSCTL_NODE(_hw, OID_AUTO, fdt, CTLFLAG_RD, 0, "Flattened Device Tree"); + +vm_paddr_t fdt_immr_pa; +vm_offset_t fdt_immr_va; +vm_offset_t fdt_immr_size; + +struct fdt_ic_list fdt_ic_list_head = SLIST_HEAD_INITIALIZER(fdt_ic_list_head); + +static int fdt_is_compatible(phandle_t, const char *); + +static int +fdt_get_range_by_busaddr(phandle_t node, u_long addr, u_long *base, + u_long *size) +{ + pcell_t ranges[32], *rangesptr; + pcell_t addr_cells, size_cells, par_addr_cells; + u_long bus_addr, par_bus_addr, pbase, psize; + int err, i, len, tuple_size, tuples; + + if (node == 0) { + *base = 0; + *size = ULONG_MAX; + return (0); + } + + if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0) + return (ENXIO); + /* + * Process 'ranges' property. + */ + par_addr_cells = fdt_parent_addr_cells(node); + if (par_addr_cells > 2) { + return (ERANGE); + } + + len = OF_getproplen(node, "ranges"); + if (len < 0) + return (-1); + if (len > sizeof(ranges)) + return (ENOMEM); + if (len == 0) { + return (fdt_get_range_by_busaddr(OF_parent(node), addr, + base, size)); + } + + if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0) + return (EINVAL); + + tuple_size = addr_cells + par_addr_cells + size_cells; + tuples = len / (tuple_size * sizeof(cell_t)); + + if (par_addr_cells > 2 || addr_cells > 2 || size_cells > 2) + return (ERANGE); + + *base = 0; + *size = 0; + + for (i = 0; i < tuples; i++) { + rangesptr = &ranges[i * tuple_size]; + + bus_addr = fdt_data_get((void *)rangesptr, addr_cells); + if (bus_addr != addr) + continue; + rangesptr += addr_cells; + + par_bus_addr = fdt_data_get((void *)rangesptr, par_addr_cells); + rangesptr += par_addr_cells; + + err = fdt_get_range_by_busaddr(OF_parent(node), par_bus_addr, + &pbase, &psize); + if (err > 0) + return (err); + if (err == 0) + *base = pbase; + else + *base = par_bus_addr; + + *size = fdt_data_get((void *)rangesptr, size_cells); + + return (0); + } + + return (EINVAL); +} + +int +fdt_get_range(phandle_t node, int range_id, u_long *base, u_long *size) +{ + pcell_t ranges[6], *rangesptr; + pcell_t addr_cells, size_cells, par_addr_cells; + u_long par_bus_addr, pbase, psize; + int err, len, tuple_size, tuples; + + if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0) + return (ENXIO); + /* + * Process 'ranges' property. + */ + par_addr_cells = fdt_parent_addr_cells(node); + if (par_addr_cells > 2) + return (ERANGE); + + len = OF_getproplen(node, "ranges"); + if (len > sizeof(ranges)) + return (ENOMEM); + if (len == 0) { + *base = 0; + *size = ULONG_MAX; + return (0); + } + + if (!(range_id < len)) + return (ERANGE); + + if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0) + return (EINVAL); + + tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells + + size_cells); + tuples = len / tuple_size; + + if (par_addr_cells > 2 || addr_cells > 2 || size_cells > 2) + return (ERANGE); + + *base = 0; + *size = 0; + rangesptr = &ranges[range_id]; + + *base = fdt_data_get((void *)rangesptr, addr_cells); + rangesptr += addr_cells; + + par_bus_addr = fdt_data_get((void *)rangesptr, par_addr_cells); + rangesptr += par_addr_cells; + + err = fdt_get_range_by_busaddr(OF_parent(node), par_bus_addr, + &pbase, &psize); + if (err == 0) + *base += pbase; + else + *base += par_bus_addr; + + *size = fdt_data_get((void *)rangesptr, size_cells); + return (0); +} + +int +fdt_immr_addr(vm_offset_t immr_va) +{ + phandle_t node; + u_long base, size; + int r; + + /* + * Try to access the SOC node directly i.e. through /aliases/. + */ + if ((node = OF_finddevice("soc")) != 0) + if (fdt_is_compatible(node, "simple-bus")) + goto moveon; + /* + * Find the node the long way. + */ + if ((node = OF_finddevice("/")) == 0) + return (ENXIO); + + if ((node = fdt_find_compatible(node, "simple-bus", 0)) == 0) + return (ENXIO); + +moveon: + if ((r = fdt_get_range(node, 0, &base, &size)) == 0) { + fdt_immr_pa = base; + fdt_immr_va = immr_va; + fdt_immr_size = size; + } + + return (r); +} + +/* + * This routine is an early-usage version of the ofw_bus_is_compatible() when + * the ofw_bus I/F is not available (like early console routines and similar). + * Note the buffer has to be on the stack since malloc() is usually not + * available in such cases either. + */ +static int +fdt_is_compatible(phandle_t node, const char *compatstr) +{ + char buf[FDT_COMPAT_LEN]; + char *compat; + int len, onelen, l, rv; + + if ((len = OF_getproplen(node, "compatible")) <= 0) + return (0); + + compat = (char *)&buf; + bzero(compat, FDT_COMPAT_LEN); + + if (OF_getprop(node, "compatible", compat, FDT_COMPAT_LEN) < 0) + return (0); + + onelen = strlen(compatstr); + rv = 0; + while (len > 0) { + if (strncasecmp(compat, compatstr, onelen) == 0) { + /* Found it. */ + rv = 1; + break; + } + /* Slide to the next sub-string. */ + l = strlen(compat) + 1; + compat += l; + len -= l; + } + + return (rv); +} + +int +fdt_is_compatible_strict(phandle_t node, const char *compatible) +{ + char compat[FDT_COMPAT_LEN]; + + if (OF_getproplen(node, "compatible") <= 0) + return (0); + + if (OF_getprop(node, "compatible", compat, FDT_COMPAT_LEN) < 0) + return (0); + + if (strncasecmp(compat, compatible, FDT_COMPAT_LEN) == 0) + /* This fits. */ + return (1); + + return (0); +} + +phandle_t +fdt_find_compatible(phandle_t start, const char *compat, int strict) +{ + phandle_t child; + + /* + * Traverse all children of 'start' node, and find first with + * matching 'compatible' property. + */ + for (child = OF_child(start); child != 0; child = OF_peer(child)) + if (fdt_is_compatible(child, compat)) { + if (strict) + if (!fdt_is_compatible_strict(child, compat)) + continue; + return (child); + } + return (0); +} + +phandle_t +fdt_depth_search_compatible(phandle_t start, const char *compat, int strict) +{ + phandle_t child, node; + + /* + * Depth-search all descendants of 'start' node, and find first with + * matching 'compatible' property. + */ + for (node = OF_child(start); node != 0; node = OF_peer(node)) { + if (fdt_is_compatible(node, compat) && + (strict == 0 || fdt_is_compatible_strict(node, compat))) { + return (node); + } + child = fdt_depth_search_compatible(node, compat, strict); + if (child != 0) + return (child); + } + return (0); +} + +int +fdt_is_enabled(phandle_t node) +{ + char *stat; + int ena, len; + + len = OF_getprop_alloc(node, "status", sizeof(char), + (void **)&stat); + + if (len <= 0) + /* It is OK if no 'status' property. */ + return (1); + + /* Anything other than 'okay' means disabled. */ + ena = 0; + if (strncmp((char *)stat, "okay", len) == 0) + ena = 1; + + OF_prop_free(stat); + return (ena); +} + +int +fdt_is_type(phandle_t node, const char *typestr) +{ + char type[FDT_TYPE_LEN]; + + if (OF_getproplen(node, "device_type") <= 0) + return (0); + + if (OF_getprop(node, "device_type", type, FDT_TYPE_LEN) < 0) + return (0); + + if (strncasecmp(type, typestr, FDT_TYPE_LEN) == 0) + /* This fits. */ + return (1); + + return (0); +} + +int +fdt_parent_addr_cells(phandle_t node) +{ + pcell_t addr_cells; + + /* Find out #address-cells of the superior bus. */ + if (OF_searchprop(OF_parent(node), "#address-cells", &addr_cells, + sizeof(addr_cells)) <= 0) + return (2); + + return ((int)fdt32_to_cpu(addr_cells)); +} + +int +fdt_pm_is_enabled(phandle_t node) +{ + int ret; + + ret = 1; + +#if defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY) + ret = fdt_pm(node); +#endif + return (ret); +} + +u_long +fdt_data_get(void *data, int cells) +{ + + if (cells == 1) + return (fdt32_to_cpu(*((uint32_t *)data))); + + return (fdt64_to_cpu(*((uint64_t *)data))); +} + +int +fdt_addrsize_cells(phandle_t node, int *addr_cells, int *size_cells) +{ + pcell_t cell; + int cell_size; + + /* + * Retrieve #{address,size}-cells. + */ + cell_size = sizeof(cell); + if (OF_getencprop(node, "#address-cells", &cell, cell_size) < cell_size) + *addr_cells = 2; + *addr_cells = (int)cell; + + if (OF_getencprop(node, "#size-cells", &cell, cell_size) < cell_size) + cell = 1; + *size_cells = (int)cell; + + if (*addr_cells > 3 || *size_cells > 2) + return (ERANGE); + return (0); +} + +int +fdt_data_to_res(pcell_t *data, int addr_cells, int size_cells, u_long *start, + u_long *count) +{ + + /* Address portion. */ + if (addr_cells > 2) + return (ERANGE); + + *start = fdt_data_get((void *)data, addr_cells); + data += addr_cells; + + /* Size portion. */ + if (size_cells > 2) + return (ERANGE); + + *count = fdt_data_get((void *)data, size_cells); + return (0); +} + +int +fdt_regsize(phandle_t node, u_long *base, u_long *size) +{ + pcell_t reg[4]; + int addr_cells, len, size_cells; + + if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells)) + return (ENXIO); + + if ((sizeof(pcell_t) * (addr_cells + size_cells)) > sizeof(reg)) + return (ENOMEM); + + len = OF_getprop(node, "reg", ®, sizeof(reg)); + if (len <= 0) + return (EINVAL); + + *base = fdt_data_get(®[0], addr_cells); + *size = fdt_data_get(®[addr_cells], size_cells); + return (0); +} + +int +fdt_reg_to_rl(phandle_t node, struct resource_list *rl) +{ + u_long end, count, start; + pcell_t *reg, *regptr; + pcell_t addr_cells, size_cells; + int tuple_size, tuples; + int i, rv; + long busaddr, bussize; + + if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0) + return (ENXIO); + if (fdt_get_range(OF_parent(node), 0, &busaddr, &bussize)) { + busaddr = 0; + bussize = 0; + } + + tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); + tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®); + debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells); + debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size); + if (tuples <= 0) + /* No 'reg' property in this node. */ + return (0); + + regptr = reg; + for (i = 0; i < tuples; i++) { + + rv = fdt_data_to_res(reg, addr_cells, size_cells, &start, + &count); + if (rv != 0) { + resource_list_free(rl); + goto out; + } + reg += addr_cells + size_cells; + + /* Calculate address range relative to base. */ + start += busaddr; + end = start + count - 1; + + debugf("reg addr start = %lx, end = %lx, count = %lx\n", start, + end, count); + + resource_list_add(rl, SYS_RES_MEMORY, i, start, end, + count); + } + rv = 0; + +out: + OF_prop_free(regptr); + return (rv); +} + +int +fdt_get_phyaddr(phandle_t node, device_t dev, int *phy_addr, void **phy_sc) +{ + phandle_t phy_node; + pcell_t phy_handle, phy_reg; + uint32_t i; + device_t parent, child; + + if (OF_getencprop(node, "phy-handle", (void *)&phy_handle, + sizeof(phy_handle)) <= 0) + return (ENXIO); + + phy_node = OF_node_from_xref(phy_handle); + + if (OF_getencprop(phy_node, "reg", (void *)&phy_reg, + sizeof(phy_reg)) <= 0) + return (ENXIO); + + *phy_addr = phy_reg; + + /* + * Search for softc used to communicate with phy. + */ + + /* + * Step 1: Search for ancestor of the phy-node with a "phy-handle" + * property set. + */ + phy_node = OF_parent(phy_node); + while (phy_node != 0) { + if (OF_getprop(phy_node, "phy-handle", (void *)&phy_handle, + sizeof(phy_handle)) > 0) + break; + phy_node = OF_parent(phy_node); + } + if (phy_node == 0) + return (ENXIO); + + /* + * Step 2: For each device with the same parent and name as ours + * compare its node with the one found in step 1, ancestor of phy + * node (stored in phy_node). + */ + parent = device_get_parent(dev); + i = 0; + child = device_find_child(parent, device_get_name(dev), i); + while (child != NULL) { + if (ofw_bus_get_node(child) == phy_node) + break; + i++; + child = device_find_child(parent, device_get_name(dev), i); + } + if (child == NULL) + return (ENXIO); + + /* + * Use softc of the device found. + */ + *phy_sc = (void *)device_get_softc(child); + + return (0); +} + +int +fdt_get_reserved_regions(struct mem_region *mr, int *mrcnt) +{ + pcell_t reserve[FDT_REG_CELLS * FDT_MEM_REGIONS]; + pcell_t *reservep; + phandle_t memory, root; + uint32_t memory_size; + int addr_cells, size_cells; + int i, max_size, res_len, rv, tuple_size, tuples; + + max_size = sizeof(reserve); + root = OF_finddevice("/"); + memory = OF_finddevice("/memory"); + if (memory == -1) { + rv = ENXIO; + goto out; + } + + if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells, + &size_cells)) != 0) + goto out; + + if (addr_cells > 2) { + rv = ERANGE; + goto out; + } + + tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); + + res_len = OF_getproplen(root, "memreserve"); + if (res_len <= 0 || res_len > sizeof(reserve)) { + rv = ERANGE; + goto out; + } + + if (OF_getprop(root, "memreserve", reserve, res_len) <= 0) { + rv = ENXIO; + goto out; + } + + memory_size = 0; + tuples = res_len / tuple_size; + reservep = (pcell_t *)&reserve; + for (i = 0; i < tuples; i++) { + + rv = fdt_data_to_res(reservep, addr_cells, size_cells, + (u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size); + + if (rv != 0) + goto out; + + reservep += addr_cells + size_cells; + } + + *mrcnt = i; + rv = 0; +out: + return (rv); +} + +int +fdt_get_mem_regions(struct mem_region *mr, int *mrcnt, uint64_t *memsize) +{ + pcell_t reg[FDT_REG_CELLS * FDT_MEM_REGIONS]; + pcell_t *regp; + phandle_t memory; + uint64_t memory_size; + int addr_cells, size_cells; + int i, max_size, reg_len, rv, tuple_size, tuples; + + max_size = sizeof(reg); + memory = OF_finddevice("/memory"); + if (memory == -1) { + rv = ENXIO; + goto out; + } + + if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells, + &size_cells)) != 0) + goto out; + + if (addr_cells > 2) { + rv = ERANGE; + goto out; + } + + tuple_size = sizeof(pcell_t) * (addr_cells + size_cells); + reg_len = OF_getproplen(memory, "reg"); + if (reg_len <= 0 || reg_len > sizeof(reg)) { + rv = ERANGE; + goto out; + } + + if (OF_getprop(memory, "reg", reg, reg_len) <= 0) { + rv = ENXIO; + goto out; + } + + memory_size = 0; + tuples = reg_len / tuple_size; + regp = (pcell_t *)® + for (i = 0; i < tuples; i++) { + + rv = fdt_data_to_res(regp, addr_cells, size_cells, + (u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size); + + if (rv != 0) + goto out; + + regp += addr_cells + size_cells; + memory_size += mr[i].mr_size; + } + + if (memory_size == 0) { + rv = ERANGE; + goto out; + } + + *mrcnt = i; + if (memsize != NULL) + *memsize = memory_size; + rv = 0; +out: + return (rv); +} + +int +fdt_get_unit(device_t dev) +{ + const char * name; + + name = ofw_bus_get_name(dev); + name = strchr(name, '@') + 1; + + return (strtol(name,NULL,0)); +} + +int +fdt_get_chosen_bootargs(char *bootargs, size_t max_size) +{ + phandle_t chosen; + + chosen = OF_finddevice("/chosen"); + if (chosen == -1) + return (ENXIO); + if (OF_getprop(chosen, "bootargs", bootargs, max_size) == -1) + return (ENXIO); + return (0); +} diff --git a/freebsd/sys/dev/fdt/fdt_common.h b/freebsd/sys/dev/fdt/fdt_common.h new file mode 100644 index 00000000..81ce4bfa --- /dev/null +++ b/freebsd/sys/dev/fdt/fdt_common.h @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 2009-2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Semihalf under sponsorship from + * the FreeBSD Foundation. + * + * 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _FDT_COMMON_H_ +#define _FDT_COMMON_H_ + +#include <sys/sysctl.h> +#include <sys/slicer.h> +#include <contrib/libfdt/libfdt_env.h> +#include <dev/ofw/ofw_bus.h> + +#define FDT_MEM_REGIONS 8 + +#define DI_MAX_INTR_NUM 32 + +struct fdt_sense_level { + enum intr_trigger trig; + enum intr_polarity pol; +}; + +#if defined(__arm__) && !defined(INTRNG) +typedef int (*fdt_pic_decode_t)(phandle_t, pcell_t *, int *, int *, int *); +extern fdt_pic_decode_t fdt_pic_table[]; +#endif + +#if defined(__arm__) || defined(__powerpc__) +typedef void (*fdt_fixup_t)(phandle_t); +struct fdt_fixup_entry { + char *model; + fdt_fixup_t handler; +}; +extern struct fdt_fixup_entry fdt_fixup_table[]; +#endif + +extern SLIST_HEAD(fdt_ic_list, fdt_ic) fdt_ic_list_head; +struct fdt_ic { + SLIST_ENTRY(fdt_ic) fdt_ics; + ihandle_t iph; + device_t dev; +}; + +extern vm_paddr_t fdt_immr_pa; +extern vm_offset_t fdt_immr_va; +extern vm_offset_t fdt_immr_size; + +struct fdt_pm_mask_entry { + char *compat; + uint32_t mask; +}; +extern struct fdt_pm_mask_entry fdt_pm_mask_table[]; + +#if defined(FDT_DTB_STATIC) +extern u_char fdt_static_dtb; +#endif + +SYSCTL_DECL(_hw_fdt); + +int fdt_addrsize_cells(phandle_t, int *, int *); +u_long fdt_data_get(void *, int); +int fdt_data_to_res(pcell_t *, int, int, u_long *, u_long *); +phandle_t fdt_find_compatible(phandle_t, const char *, int); +phandle_t fdt_depth_search_compatible(phandle_t, const char *, int); +int fdt_get_mem_regions(struct mem_region *, int *, uint64_t *); +int fdt_get_reserved_regions(struct mem_region *, int *); +int fdt_get_phyaddr(phandle_t, device_t, int *, void **); +int fdt_get_range(phandle_t, int, u_long *, u_long *); +int fdt_immr_addr(vm_offset_t); +int fdt_regsize(phandle_t, u_long *, u_long *); +int fdt_is_compatible_strict(phandle_t, const char *); +int fdt_is_enabled(phandle_t); +int fdt_pm_is_enabled(phandle_t); +int fdt_is_type(phandle_t, const char *); +int fdt_parent_addr_cells(phandle_t); +int fdt_reg_to_rl(phandle_t, struct resource_list *); +int fdt_pm(phandle_t); +int fdt_get_unit(device_t); +int fdt_get_chosen_bootargs(char *bootargs, size_t max_size); + +#endif /* _FDT_COMMON_H_ */ diff --git a/freebsd/sys/dev/fdt/simplebus.c b/freebsd/sys/dev/fdt/simplebus.c new file mode 100644 index 00000000..d981d065 --- /dev/null +++ b/freebsd/sys/dev/fdt/simplebus.c @@ -0,0 +1,431 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2013 Nathan Whitehorn + * All rights reserved. + * + * 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 AUTHOR 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 AUTHOR 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/rman.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/fdt/simplebus.h> + +/* + * Bus interface. + */ +static int simplebus_probe(device_t dev); +static int simplebus_attach(device_t dev); +static struct resource *simplebus_alloc_resource(device_t, device_t, int, + int *, rman_res_t, rman_res_t, rman_res_t, u_int); +static void simplebus_probe_nomatch(device_t bus, device_t child); +static int simplebus_print_child(device_t bus, device_t child); +static device_t simplebus_add_child(device_t dev, u_int order, + const char *name, int unit); +static struct resource_list *simplebus_get_resource_list(device_t bus, + device_t child); +/* + * ofw_bus interface + */ +static const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus, + device_t child); + +/* + * local methods + */ + +static int simplebus_fill_ranges(phandle_t node, + struct simplebus_softc *sc); + +/* + * Driver methods. + */ +static device_method_t simplebus_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, simplebus_probe), + DEVMETHOD(device_attach, simplebus_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* Bus interface */ + DEVMETHOD(bus_add_child, simplebus_add_child), + DEVMETHOD(bus_print_child, simplebus_print_child), + DEVMETHOD(bus_probe_nomatch, simplebus_probe_nomatch), + DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), + DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), + DEVMETHOD(bus_alloc_resource, simplebus_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), + DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), + DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), + DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), + DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, simplebus_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(simplebus, simplebus_driver, simplebus_methods, + sizeof(struct simplebus_softc)); + +static devclass_t simplebus_devclass; +EARLY_DRIVER_MODULE(simplebus, ofwbus, simplebus_driver, simplebus_devclass, + 0, 0, BUS_PASS_BUS); +EARLY_DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass, + 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); + +static int +simplebus_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + /* + * FDT data puts a "simple-bus" compatible string on many things that + * have children but aren't really busses in our world. Without a + * ranges property we will fail to attach, so just fail to probe too. + */ + if (!(ofw_bus_is_compatible(dev, "simple-bus") && + ofw_bus_has_prop(dev, "ranges")) && + (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev), + "soc") != 0)) + return (ENXIO); + + device_set_desc(dev, "Flattened device tree simple bus"); + + return (BUS_PROBE_GENERIC); +} + +static int +simplebus_attach(device_t dev) +{ + struct simplebus_softc *sc; + phandle_t node; + + sc = device_get_softc(dev); + simplebus_init(dev, 0); + if (simplebus_fill_ranges(sc->node, sc) < 0) { + device_printf(dev, "could not get ranges\n"); + return (ENXIO); + } + + /* + * In principle, simplebus could have an interrupt map, but ignore that + * for now + */ + + for (node = OF_child(sc->node); node > 0; node = OF_peer(node)) + simplebus_add_device(dev, node, 0, NULL, -1, NULL); + return (bus_generic_attach(dev)); +} + +void +simplebus_init(device_t dev, phandle_t node) +{ + struct simplebus_softc *sc; + + sc = device_get_softc(dev); + if (node == 0) + node = ofw_bus_get_node(dev); + sc->dev = dev; + sc->node = node; + + /* + * Some important numbers + */ + sc->acells = 2; + OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells)); + sc->scells = 1; + OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells)); +} + +static int +simplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc) +{ + int host_address_cells; + cell_t *base_ranges; + ssize_t nbase_ranges; + int err; + int i, j, k; + + err = OF_searchencprop(OF_parent(node), "#address-cells", + &host_address_cells, sizeof(host_address_cells)); + if (err <= 0) + return (-1); + + nbase_ranges = OF_getproplen(node, "ranges"); + if (nbase_ranges < 0) + return (-1); + sc->nranges = nbase_ranges / sizeof(cell_t) / + (sc->acells + host_address_cells + sc->scells); + if (sc->nranges == 0) + return (0); + + sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), + M_DEVBUF, M_WAITOK); + base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); + OF_getencprop(node, "ranges", base_ranges, nbase_ranges); + + for (i = 0, j = 0; i < sc->nranges; i++) { + sc->ranges[i].bus = 0; + for (k = 0; k < sc->acells; k++) { + sc->ranges[i].bus <<= 32; + sc->ranges[i].bus |= base_ranges[j++]; + } + sc->ranges[i].host = 0; + for (k = 0; k < host_address_cells; k++) { + sc->ranges[i].host <<= 32; + sc->ranges[i].host |= base_ranges[j++]; + } + sc->ranges[i].size = 0; + for (k = 0; k < sc->scells; k++) { + sc->ranges[i].size <<= 32; + sc->ranges[i].size |= base_ranges[j++]; + } + } + + free(base_ranges, M_DEVBUF); + return (sc->nranges); +} + +struct simplebus_devinfo * +simplebus_setup_dinfo(device_t dev, phandle_t node, + struct simplebus_devinfo *di) +{ + struct simplebus_softc *sc; + struct simplebus_devinfo *ndi; + + sc = device_get_softc(dev); + if (di == NULL) + ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); + else + ndi = di; + if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) { + if (di == NULL) + free(ndi, M_DEVBUF); + return (NULL); + } + + resource_list_init(&ndi->rl); + ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells, &ndi->rl); + ofw_bus_intr_to_rl(dev, node, &ndi->rl, NULL); + + return (ndi); +} + +device_t +simplebus_add_device(device_t dev, phandle_t node, u_int order, + const char *name, int unit, struct simplebus_devinfo *di) +{ + struct simplebus_devinfo *ndi; + device_t cdev; + + if ((ndi = simplebus_setup_dinfo(dev, node, di)) == NULL) + return (NULL); + cdev = device_add_child_ordered(dev, order, name, unit); + if (cdev == NULL) { + device_printf(dev, "<%s>: device_add_child failed\n", + ndi->obdinfo.obd_name); + resource_list_free(&ndi->rl); + ofw_bus_gen_destroy_devinfo(&ndi->obdinfo); + if (di == NULL) + free(ndi, M_DEVBUF); + return (NULL); + } + device_set_ivars(cdev, ndi); + + return(cdev); +} + +static device_t +simplebus_add_child(device_t dev, u_int order, const char *name, int unit) +{ + device_t cdev; + struct simplebus_devinfo *ndi; + + cdev = device_add_child_ordered(dev, order, name, unit); + if (cdev == NULL) + return (NULL); + + ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); + ndi->obdinfo.obd_node = -1; + resource_list_init(&ndi->rl); + device_set_ivars(cdev, ndi); + + return (cdev); +} + +static const struct ofw_bus_devinfo * +simplebus_get_devinfo(device_t bus __unused, device_t child) +{ + struct simplebus_devinfo *ndi; + + ndi = device_get_ivars(child); + if (ndi == NULL) + return (NULL); + return (&ndi->obdinfo); +} + +static struct resource_list * +simplebus_get_resource_list(device_t bus __unused, device_t child) +{ + struct simplebus_devinfo *ndi; + + ndi = device_get_ivars(child); + if (ndi == NULL) + return (NULL); + return (&ndi->rl); +} + +static struct resource * +simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + struct simplebus_softc *sc; + struct simplebus_devinfo *di; + struct resource_list_entry *rle; + int j; + + sc = device_get_softc(bus); + + /* + * Request for the default allocation with a given rid: use resource + * list stored in the local device info. + */ + if (RMAN_IS_DEFAULT_RANGE(start, end)) { + if ((di = device_get_ivars(child)) == NULL) + return (NULL); + + if (type == SYS_RES_IOPORT) + type = SYS_RES_MEMORY; + + rle = resource_list_find(&di->rl, type, *rid); + if (rle == NULL) { + if (bootverbose) + device_printf(bus, "no default resources for " + "rid = %d, type = %d\n", *rid, type); + return (NULL); + } + start = rle->start; + end = rle->end; + count = rle->count; + } + + if (type == SYS_RES_MEMORY) { + /* Remap through ranges property */ + for (j = 0; j < sc->nranges; j++) { + if (start >= sc->ranges[j].bus && end < + sc->ranges[j].bus + sc->ranges[j].size) { + start -= sc->ranges[j].bus; + start += sc->ranges[j].host; + end -= sc->ranges[j].bus; + end += sc->ranges[j].host; + break; + } + } + if (j == sc->nranges && sc->nranges != 0) { + if (bootverbose) + device_printf(bus, "Could not map resource " + "%#jx-%#jx\n", start, end); + + return (NULL); + } + } + + return (bus_generic_alloc_resource(bus, child, type, rid, start, end, + count, flags)); +} + +static int +simplebus_print_res(struct simplebus_devinfo *di) +{ + int rv; + + if (di == NULL) + return (0); + rv = 0; + rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#jx"); + rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%jd"); + return (rv); +} + +static void +simplebus_probe_nomatch(device_t bus, device_t child) +{ + const char *name, *type, *compat; + + if (!bootverbose) + return; + + compat = ofw_bus_get_compat(child); + if (compat == NULL) + return; + name = ofw_bus_get_name(child); + type = ofw_bus_get_type(child); + + device_printf(bus, "<%s>", name != NULL ? name : "unknown"); + simplebus_print_res(device_get_ivars(child)); + if (!ofw_bus_status_okay(child)) + printf(" disabled"); + if (type) + printf(" type %s", type); + printf(" compat %s (no driver attached)\n", compat); +} + +static int +simplebus_print_child(device_t bus, device_t child) +{ + int rv; + + rv = bus_print_child_header(bus, child); + rv += simplebus_print_res(device_get_ivars(child)); + if (!ofw_bus_status_okay(child)) + rv += printf(" disabled"); + rv += bus_print_child_footer(bus, child); + return (rv); +} diff --git a/freebsd/sys/dev/fdt/simplebus.h b/freebsd/sys/dev/fdt/simplebus.h new file mode 100644 index 00000000..caccf162 --- /dev/null +++ b/freebsd/sys/dev/fdt/simplebus.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2013 Nathan Whitehorn + * All rights reserved. + * + * 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 AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _FDT_SIMPLEBUS_H +#define _FDT_SIMPLEBUS_H + +#include <dev/ofw/ofw_bus.h> + +/* FDT simplebus */ +DECLARE_CLASS(simplebus_driver); + +struct simplebus_range { + uint64_t bus; + uint64_t host; + uint64_t size; +}; + +/* devinfo and softc */ +struct simplebus_softc { + device_t dev; + phandle_t node; + + struct simplebus_range *ranges; + int nranges; + + pcell_t acells, scells; +}; + +struct simplebus_devinfo { + struct ofw_bus_devinfo obdinfo; + struct resource_list rl; +}; + +void simplebus_init(device_t dev, phandle_t node); +device_t simplebus_add_device(device_t dev, phandle_t node, u_int order, + const char *name, int unit, struct simplebus_devinfo *di); +struct simplebus_devinfo *simplebus_setup_dinfo(device_t dev, phandle_t node, + struct simplebus_devinfo *di); +#endif /* _FDT_SIMPLEBUS_H */ |