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 | |
parent | media01: Add cpuinfo command (diff) | |
download | rtems-libbsd-f0dd0c506a706542e1a6b1c3e78e6c9809d31085.tar.bz2 |
FDT(4): Import from FreeBSD
Diffstat (limited to 'freebsd')
-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 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/ofw_bus.h | 79 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/ofw_bus_subr.c | 953 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/ofw_bus_subr.h | 145 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/ofw_fdt.c | 490 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/ofw_pci.h | 103 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/ofw_subr.c | 246 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/ofw_subr.h | 51 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/ofwbus.c | 297 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/ofwvar.h | 89 | ||||
-rw-r--r-- | freebsd/sys/dev/ofw/openfirm.c | 807 | ||||
-rw-r--r-- | freebsd/sys/dev/tsec/if_tsec_fdt.c | 390 | ||||
-rw-r--r-- | freebsd/sys/sys/slicer.h | 52 |
16 files changed, 5048 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 */ diff --git a/freebsd/sys/dev/ofw/ofw_bus.h b/freebsd/sys/dev/ofw/ofw_bus.h new file mode 100644 index 00000000..dff9a2b8 --- /dev/null +++ b/freebsd/sys/dev/ofw/ofw_bus.h @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 2001, 2003 by Thomas Moestl <tmm@FreeBSD.org> + * Copyright (c) 2004 by Marius Strobl <marius@FreeBSD.org> + * 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 ``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 _DEV_OFW_OFW_BUS_H_ +#define _DEV_OFW_OFW_BUS_H_ + +#include <sys/bus.h> + +#include <dev/ofw/openfirm.h> + +#include <rtems/bsd/local/ofw_bus_if.h> + +static __inline const char * +ofw_bus_get_compat(device_t dev) +{ + + return (OFW_BUS_GET_COMPAT(device_get_parent(dev), dev)); +} + +static __inline const char * +ofw_bus_get_model(device_t dev) +{ + + return (OFW_BUS_GET_MODEL(device_get_parent(dev), dev)); +} + +static __inline const char * +ofw_bus_get_name(device_t dev) +{ + + return (OFW_BUS_GET_NAME(device_get_parent(dev), dev)); +} + +static __inline phandle_t +ofw_bus_get_node(device_t dev) +{ + + return (OFW_BUS_GET_NODE(device_get_parent(dev), dev)); +} + +static __inline const char * +ofw_bus_get_type(device_t dev) +{ + + return (OFW_BUS_GET_TYPE(device_get_parent(dev), dev)); +} + +static __inline int +ofw_bus_map_intr(device_t dev, phandle_t iparent, int icells, pcell_t *intr) +{ + return (OFW_BUS_MAP_INTR(dev, dev, iparent, icells, intr)); +} + +#endif /* !_DEV_OFW_OFW_BUS_H_ */ diff --git a/freebsd/sys/dev/ofw/ofw_bus_subr.c b/freebsd/sys/dev/ofw/ofw_bus_subr.c new file mode 100644 index 00000000..79852afc --- /dev/null +++ b/freebsd/sys/dev/ofw/ofw_bus_subr.c @@ -0,0 +1,953 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>. + * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org> + * 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, + * without modification, immediately at the beginning of the file. + * 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/local/opt_platform.h> +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <rtems/bsd/sys/errno.h> +#include <sys/libkern.h> + +#include <machine/resource.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> + +#define OFW_COMPAT_LEN 255 + +int +ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node) +{ + + if (obd == NULL) + return (ENOMEM); + /* The 'name' property is considered mandatory. */ + if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1) + return (EINVAL); + OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat); + OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type); + OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model); + OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status); + obd->obd_node = node; + return (0); +} + +void +ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd) +{ + + if (obd == NULL) + return; + if (obd->obd_compat != NULL) + free(obd->obd_compat, M_OFWPROP); + if (obd->obd_model != NULL) + free(obd->obd_model, M_OFWPROP); + if (obd->obd_name != NULL) + free(obd->obd_name, M_OFWPROP); + if (obd->obd_type != NULL) + free(obd->obd_type, M_OFWPROP); + if (obd->obd_status != NULL) + free(obd->obd_status, M_OFWPROP); +} + +int +ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf, + size_t buflen) +{ + + if (ofw_bus_get_name(child) != NULL) { + strlcat(buf, "name=", buflen); + strlcat(buf, ofw_bus_get_name(child), buflen); + } + + if (ofw_bus_get_compat(child) != NULL) { + strlcat(buf, " compat=", buflen); + strlcat(buf, ofw_bus_get_compat(child), buflen); + } + return (0); +}; + +const char * +ofw_bus_gen_get_compat(device_t bus, device_t dev) +{ + const struct ofw_bus_devinfo *obd; + + obd = OFW_BUS_GET_DEVINFO(bus, dev); + if (obd == NULL) + return (NULL); + return (obd->obd_compat); +} + +const char * +ofw_bus_gen_get_model(device_t bus, device_t dev) +{ + const struct ofw_bus_devinfo *obd; + + obd = OFW_BUS_GET_DEVINFO(bus, dev); + if (obd == NULL) + return (NULL); + return (obd->obd_model); +} + +const char * +ofw_bus_gen_get_name(device_t bus, device_t dev) +{ + const struct ofw_bus_devinfo *obd; + + obd = OFW_BUS_GET_DEVINFO(bus, dev); + if (obd == NULL) + return (NULL); + return (obd->obd_name); +} + +phandle_t +ofw_bus_gen_get_node(device_t bus, device_t dev) +{ + const struct ofw_bus_devinfo *obd; + + obd = OFW_BUS_GET_DEVINFO(bus, dev); + if (obd == NULL) + return (0); + return (obd->obd_node); +} + +const char * +ofw_bus_gen_get_type(device_t bus, device_t dev) +{ + const struct ofw_bus_devinfo *obd; + + obd = OFW_BUS_GET_DEVINFO(bus, dev); + if (obd == NULL) + return (NULL); + return (obd->obd_type); +} + +const char * +ofw_bus_get_status(device_t dev) +{ + const struct ofw_bus_devinfo *obd; + + obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev); + if (obd == NULL) + return (NULL); + + return (obd->obd_status); +} + +int +ofw_bus_status_okay(device_t dev) +{ + const char *status; + + status = ofw_bus_get_status(dev); + if (status == NULL || strcmp(status, "okay") == 0 || + strcmp(status, "ok") == 0) + return (1); + + return (0); +} + +static int +ofw_bus_node_is_compatible_int(const char *compat, int len, + const char *onecompat) +{ + int onelen, l, ret; + + onelen = strlen(onecompat); + + ret = 0; + while (len > 0) { + if (strlen(compat) == onelen && + strncasecmp(compat, onecompat, onelen) == 0) { + /* Found it. */ + ret = 1; + break; + } + + /* Slide to the next sub-string. */ + l = strlen(compat) + 1; + compat += l; + len -= l; + } + + return (ret); +} + +int +ofw_bus_node_is_compatible(phandle_t node, const char *compatstr) +{ + char compat[OFW_COMPAT_LEN]; + int len, rv; + + if ((len = OF_getproplen(node, "compatible")) <= 0) + return (0); + + bzero(compat, OFW_COMPAT_LEN); + + if (OF_getprop(node, "compatible", compat, OFW_COMPAT_LEN) < 0) + return (0); + + rv = ofw_bus_node_is_compatible_int(compat, len, compatstr); + + return (rv); +} + +int +ofw_bus_is_compatible(device_t dev, const char *onecompat) +{ + phandle_t node; + const char *compat; + int len; + + if ((compat = ofw_bus_get_compat(dev)) == NULL) + return (0); + + if ((node = ofw_bus_get_node(dev)) == -1) + return (0); + + /* Get total 'compatible' prop len */ + if ((len = OF_getproplen(node, "compatible")) <= 0) + return (0); + + return (ofw_bus_node_is_compatible_int(compat, len, onecompat)); +} + +int +ofw_bus_is_compatible_strict(device_t dev, const char *compatible) +{ + const char *compat; + size_t len; + + if ((compat = ofw_bus_get_compat(dev)) == NULL) + return (0); + + len = strlen(compatible); + if (strlen(compat) == len && + strncasecmp(compat, compatible, len) == 0) + return (1); + + return (0); +} + +const struct ofw_compat_data * +ofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat) +{ + + if (compat == NULL) + return NULL; + + for (; compat->ocd_str != NULL; ++compat) { + if (ofw_bus_is_compatible(dev, compat->ocd_str)) + break; + } + + return (compat); +} + +int +ofw_bus_has_prop(device_t dev, const char *propname) +{ + phandle_t node; + + if ((node = ofw_bus_get_node(dev)) == -1) + return (0); + + return (OF_hasprop(node, propname)); +} + +void +ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz) +{ + pcell_t addrc; + int msksz; + + if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1) + addrc = 2; + ii->opi_addrc = addrc * sizeof(pcell_t); + + ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1, + (void **)&ii->opi_imap); + if (ii->opi_imapsz > 0) { + msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1, + (void **)&ii->opi_imapmsk); + /* + * Failure to get the mask is ignored; a full mask is used + * then. We barf on bad mask sizes, however. + */ + if (msksz != -1 && msksz != ii->opi_addrc + intrsz) + panic("ofw_bus_setup_iinfo: bad interrupt-map-mask " + "property!"); + } +} + +int +ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg, + int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz, + phandle_t *iparent) +{ + uint8_t maskbuf[regsz + pintrsz]; + int rv; + + if (ii->opi_imapsz <= 0) + return (0); + KASSERT(regsz >= ii->opi_addrc, + ("ofw_bus_lookup_imap: register size too small: %d < %d", + regsz, ii->opi_addrc)); + if (node != -1) { + rv = OF_getencprop(node, "reg", reg, regsz); + if (rv < regsz) + panic("ofw_bus_lookup_imap: cannot get reg property"); + } + return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc, + ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr, + mintrsz, iparent)); +} + +/* + * Map an interrupt using the firmware reg, interrupt-map and + * interrupt-map-mask properties. + * The interrupt property to be mapped must be of size intrsz, and pointed to + * by intr. The regs property of the node for which the mapping is done must + * be passed as regs. This property is an array of register specifications; + * the size of the address part of such a specification must be passed as + * physsz. Only the first element of the property is used. + * imap and imapsz hold the interrupt mask and it's size. + * imapmsk is a pointer to the interrupt-map-mask property, which must have + * a size of physsz + intrsz; it may be NULL, in which case a full mask is + * assumed. + * maskbuf must point to a buffer of length physsz + intrsz. + * The interrupt is returned in result, which must point to a buffer of length + * rintrsz (which gives the expected size of the mapped interrupt). + * Returns number of cells in the interrupt if a mapping was found, 0 otherwise. + */ +int +ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz, + void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result, + int rintrsz, phandle_t *iparent) +{ + phandle_t parent; + uint8_t *ref = maskbuf; + uint8_t *uiintr = intr; + uint8_t *uiregs = regs; + uint8_t *uiimapmsk = imapmsk; + uint8_t *mptr; + pcell_t paddrsz; + pcell_t pintrsz; + int i, rsz, tsz; + + rsz = -1; + if (imapmsk != NULL) { + for (i = 0; i < physsz; i++) + ref[i] = uiregs[i] & uiimapmsk[i]; + for (i = 0; i < intrsz; i++) + ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i]; + } else { + bcopy(regs, ref, physsz); + bcopy(intr, ref + physsz, intrsz); + } + + mptr = imap; + i = imapsz; + paddrsz = 0; + while (i > 0) { + bcopy(mptr + physsz + intrsz, &parent, sizeof(parent)); +#ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS + /* + * Find if we need to read the parent address data. + * CHRP-derived OF bindings, including ePAPR-compliant FDTs, + * use this as an optional part of the specifier. + */ + if (OF_getencprop(OF_node_from_xref(parent), + "#address-cells", &paddrsz, sizeof(paddrsz)) == -1) + paddrsz = 0; /* default */ + paddrsz *= sizeof(pcell_t); +#endif + + if (OF_searchencprop(OF_node_from_xref(parent), + "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1) + pintrsz = 1; /* default */ + pintrsz *= sizeof(pcell_t); + + /* Compute the map stride size. */ + tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz; + KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map")); + + if (bcmp(ref, mptr, physsz + intrsz) == 0) { + bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz, + result, MIN(rintrsz, pintrsz)); + + if (iparent != NULL) + *iparent = parent; + return (pintrsz/sizeof(pcell_t)); + } + mptr += tsz; + i -= tsz; + } + return (0); +} + +int +ofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent, + uint32_t *msi_rid) +{ + pcell_t *map, mask, msi_base, rid_base, rid_length; + ssize_t len; + uint32_t masked_rid, rid; + int err, i; + + /* TODO: This should be OF_searchprop_alloc if we had it */ + len = OF_getencprop_alloc(node, "msi-map", sizeof(*map), (void **)&map); + if (len < 0) { + if (msi_parent != NULL) { + *msi_parent = 0; + OF_getencprop(node, "msi-parent", msi_parent, + sizeof(*msi_parent)); + } + if (msi_rid != NULL) + *msi_rid = pci_rid; + return (0); + } + + err = ENOENT; + rid = 0; + mask = 0xffffffff; + OF_getencprop(node, "msi-map-mask", &mask, sizeof(mask)); + + masked_rid = pci_rid & mask; + for (i = 0; i < len; i += 4) { + rid_base = map[i + 0]; + rid_length = map[i + 3]; + + if (masked_rid < rid_base || + masked_rid >= (rid_base + rid_length)) + continue; + + msi_base = map[i + 2]; + + if (msi_parent != NULL) + *msi_parent = map[i + 1]; + if (msi_rid != NULL) + *msi_rid = masked_rid - rid_base + msi_base; + err = 0; + break; + } + + free(map, M_OFWPROP); + + return (err); +} + +int +ofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells, + struct resource_list *rl) +{ + uint64_t phys, size; + ssize_t i, j, rid, nreg, ret; + uint32_t *reg; + char *name; + + /* + * This may be just redundant when having ofw_bus_devinfo + * but makes this routine independent of it. + */ + ret = OF_getprop_alloc(node, "name", sizeof(*name), (void **)&name); + if (ret == -1) + name = NULL; + + ret = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)®); + nreg = (ret == -1) ? 0 : ret; + + if (nreg % (acells + scells) != 0) { + if (bootverbose) + device_printf(dev, "Malformed reg property on <%s>\n", + (name == NULL) ? "unknown" : name); + nreg = 0; + } + + for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) { + phys = size = 0; + for (j = 0; j < acells; j++) { + phys <<= 32; + phys |= reg[i + j]; + } + for (j = 0; j < scells; j++) { + size <<= 32; + size |= reg[i + acells + j]; + } + /* Skip the dummy reg property of glue devices like ssm(4). */ + if (size != 0) + resource_list_add(rl, SYS_RES_MEMORY, rid, + phys, phys + size - 1, size); + } + free(name, M_OFWPROP); + free(reg, M_OFWPROP); + + return (0); +} + +/* + * Get interrupt parent for given node. + * Returns 0 if interrupt parent doesn't exist. + */ +phandle_t +ofw_bus_find_iparent(phandle_t node) +{ + phandle_t iparent; + + if (OF_searchencprop(node, "interrupt-parent", &iparent, + sizeof(iparent)) == -1) { + for (iparent = node; iparent != 0; + iparent = OF_parent(iparent)) { + if (OF_hasprop(iparent, "interrupt-controller")) + break; + } + iparent = OF_xref_from_node(iparent); + } + return (iparent); +} + +int +ofw_bus_intr_to_rl(device_t dev, phandle_t node, + struct resource_list *rl, int *rlen) +{ + phandle_t iparent; + uint32_t icells, *intr; + int err, i, irqnum, nintr, rid; + boolean_t extended; + + nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), + (void **)&intr); + if (nintr > 0) { + iparent = ofw_bus_find_iparent(node); + if (iparent == 0) { + device_printf(dev, "No interrupt-parent found, " + "assuming direct parent\n"); + iparent = OF_parent(node); + iparent = OF_xref_from_node(iparent); + } + if (OF_searchencprop(OF_node_from_xref(iparent), + "#interrupt-cells", &icells, sizeof(icells)) == -1) { + device_printf(dev, "Missing #interrupt-cells " + "property, assuming <1>\n"); + icells = 1; + } + if (icells < 1 || icells > nintr) { + device_printf(dev, "Invalid #interrupt-cells property " + "value <%d>, assuming <1>\n", icells); + icells = 1; + } + extended = false; + } else { + nintr = OF_getencprop_alloc(node, "interrupts-extended", + sizeof(*intr), (void **)&intr); + if (nintr <= 0) + return (0); + extended = true; + } + err = 0; + rid = 0; + for (i = 0; i < nintr; i += icells) { + if (extended) { + iparent = intr[i++]; + if (OF_searchencprop(OF_node_from_xref(iparent), + "#interrupt-cells", &icells, sizeof(icells)) == -1) { + device_printf(dev, "Missing #interrupt-cells " + "property\n"); + err = ENOENT; + break; + } + if (icells < 1 || (i + icells) > nintr) { + device_printf(dev, "Invalid #interrupt-cells " + "property value <%d>\n", icells); + err = ERANGE; + break; + } + } + irqnum = ofw_bus_map_intr(dev, iparent, icells, &intr[i]); + resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1); + } + if (rlen != NULL) + *rlen = rid; + free(intr, M_OFWPROP); + return (err); +} + +int +ofw_bus_intr_by_rid(device_t dev, phandle_t node, int wanted_rid, + phandle_t *producer, int *ncells, pcell_t **cells) +{ + phandle_t iparent; + uint32_t icells, *intr; + int err, i, nintr, rid; + boolean_t extended; + + nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr), + (void **)&intr); + if (nintr > 0) { + iparent = ofw_bus_find_iparent(node); + if (iparent == 0) { + device_printf(dev, "No interrupt-parent found, " + "assuming direct parent\n"); + iparent = OF_parent(node); + iparent = OF_xref_from_node(iparent); + } + if (OF_searchencprop(OF_node_from_xref(iparent), + "#interrupt-cells", &icells, sizeof(icells)) == -1) { + device_printf(dev, "Missing #interrupt-cells " + "property, assuming <1>\n"); + icells = 1; + } + if (icells < 1 || icells > nintr) { + device_printf(dev, "Invalid #interrupt-cells property " + "value <%d>, assuming <1>\n", icells); + icells = 1; + } + extended = false; + } else { + nintr = OF_getencprop_alloc(node, "interrupts-extended", + sizeof(*intr), (void **)&intr); + if (nintr <= 0) + return (ESRCH); + extended = true; + } + err = ESRCH; + rid = 0; + for (i = 0; i < nintr; i += icells, rid++) { + if (extended) { + iparent = intr[i++]; + if (OF_searchencprop(OF_node_from_xref(iparent), + "#interrupt-cells", &icells, sizeof(icells)) == -1) { + device_printf(dev, "Missing #interrupt-cells " + "property\n"); + err = ENOENT; + break; + } + if (icells < 1 || (i + icells) > nintr) { + device_printf(dev, "Invalid #interrupt-cells " + "property value <%d>\n", icells); + err = ERANGE; + break; + } + } + if (rid == wanted_rid) { + *cells = malloc(icells * sizeof(**cells), M_OFWPROP, + M_WAITOK); + *producer = iparent; + *ncells= icells; + memcpy(*cells, intr + i, icells * sizeof(**cells)); + err = 0; + break; + } + } + free(intr, M_OFWPROP); + return (err); +} + +phandle_t +ofw_bus_find_child(phandle_t start, const char *child_name) +{ + char *name; + int ret; + phandle_t child; + + for (child = OF_child(start); child != 0; child = OF_peer(child)) { + ret = OF_getprop_alloc(child, "name", sizeof(*name), (void **)&name); + if (ret == -1) + continue; + if (strcmp(name, child_name) == 0) { + free(name, M_OFWPROP); + return (child); + } + + free(name, M_OFWPROP); + } + + return (0); +} + +phandle_t +ofw_bus_find_compatible(phandle_t node, const char *onecompat) +{ + phandle_t child, ret; + void *compat; + int len; + + /* + * Traverse all children of 'start' node, and find first with + * matching 'compatible' property. + */ + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + len = OF_getprop_alloc(child, "compatible", 1, &compat); + if (len >= 0) { + ret = ofw_bus_node_is_compatible_int(compat, len, + onecompat); + free(compat, M_OFWPROP); + if (ret != 0) + return (child); + } + + ret = ofw_bus_find_compatible(child, onecompat); + if (ret != 0) + return (ret); + } + return (0); +} + +/** + * @brief Return child of bus whose phandle is node + * + * A direct child of @p will be returned if it its phandle in the + * OFW tree is @p node. Otherwise, NULL is returned. + * + * @param bus The bus to examine + * @param node The phandle_t to look for. + */ +device_t +ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node) +{ + device_t *children, retval, child; + int nkid, i; + + /* + * Nothing can match the flag value for no node. + */ + if (node == -1) + return (NULL); + + /* + * Search the children for a match. We microoptimize + * a bit by not using ofw_bus_get since we already know + * the parent. We do not recurse. + */ + if (device_get_children(bus, &children, &nkid) != 0) + return (NULL); + retval = NULL; + for (i = 0; i < nkid; i++) { + child = children[i]; + if (OFW_BUS_GET_NODE(bus, child) == node) { + retval = child; + break; + } + } + free(children, M_TEMP); + + return (retval); +} + +/* + * Parse property that contain list of xrefs and values + * (like standard "clocks" and "resets" properties) + * Input arguments: + * node - consumers device node + * list_name - name of parsed list - "clocks" + * cells_name - name of size property - "#clock-cells" + * idx - the index of the requested list entry, or, if -1, an indication + * to return the number of entries in the parsed list. + * Output arguments: + * producer - handle of producer + * ncells - number of cells in result or the number of items in the list when + * idx == -1. + * cells - array of decoded cells + */ +static int +ofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name, + const char *cells_name, int idx, phandle_t *producer, int *ncells, + pcell_t **cells) +{ + phandle_t pnode; + phandle_t *elems; + uint32_t pcells; + int rv, i, j, nelems, cnt; + + elems = NULL; + nelems = OF_getencprop_alloc(node, list_name, sizeof(*elems), + (void **)&elems); + if (nelems <= 0) + return (ENOENT); + rv = (idx == -1) ? 0 : ENOENT; + for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) { + pnode = elems[i++]; + if (OF_getencprop(OF_node_from_xref(pnode), + cells_name, &pcells, sizeof(pcells)) == -1) { + printf("Missing %s property\n", cells_name); + rv = ENOENT; + break; + } + + if ((i + pcells) > nelems) { + printf("Invalid %s property value <%d>\n", cells_name, + pcells); + rv = ERANGE; + break; + } + if (cnt == idx) { + *cells= malloc(pcells * sizeof(**cells), M_OFWPROP, + M_WAITOK); + *producer = pnode; + *ncells = pcells; + for (j = 0; j < pcells; j++) + (*cells)[j] = elems[i + j]; + rv = 0; + break; + } + } + if (elems != NULL) + free(elems, M_OFWPROP); + if (idx == -1 && rv == 0) + *ncells = cnt; + return (rv); +} + +/* + * Parse property that contain list of xrefs and values + * (like standard "clocks" and "resets" properties) + * Input arguments: + * node - consumers device node + * list_name - name of parsed list - "clocks" + * cells_name - name of size property - "#clock-cells" + * idx - the index of the requested list entry (>= 0) + * Output arguments: + * producer - handle of producer + * ncells - number of cells in result + * cells - array of decoded cells + */ +int +ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name, + const char *cells_name, int idx, phandle_t *producer, int *ncells, + pcell_t **cells) +{ + + KASSERT(idx >= 0, + ("ofw_bus_parse_xref_list_alloc: negative index supplied")); + + return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, + idx, producer, ncells, cells)); +} + +/* + * Parse property that contain list of xrefs and values + * (like standard "clocks" and "resets" properties) + * and determine the number of items in the list + * Input arguments: + * node - consumers device node + * list_name - name of parsed list - "clocks" + * cells_name - name of size property - "#clock-cells" + * Output arguments: + * count - number of items in list + */ +int +ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name, + const char *cells_name, int *count) +{ + + return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name, + -1, NULL, count, NULL)); +} + +/* + * Find index of string in string list property (case sensitive). + */ +int +ofw_bus_find_string_index(phandle_t node, const char *list_name, + const char *name, int *idx) +{ + char *elems; + int rv, i, cnt, nelems; + + elems = NULL; + nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems); + if (nelems <= 0) + return (ENOENT); + + rv = ENOENT; + for (i = 0, cnt = 0; i < nelems; cnt++) { + if (strcmp(elems + i, name) == 0) { + *idx = cnt; + rv = 0; + break; + } + i += strlen(elems + i) + 1; + } + + if (elems != NULL) + free(elems, M_OFWPROP); + return (rv); +} + +/* + * Create zero terminated array of strings from string list property. + */ +int +ofw_bus_string_list_to_array(phandle_t node, const char *list_name, + const char ***out_array) +{ + char *elems, *tptr; + const char **array; + int i, cnt, nelems, len; + + elems = NULL; + nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems); + if (nelems <= 0) + return (nelems); + + /* Count number of strings. */ + for (i = 0, cnt = 0; i < nelems; cnt++) + i += strlen(elems + i) + 1; + + /* Allocate space for arrays and all strings. */ + array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP, + M_WAITOK); + + /* Get address of first string. */ + tptr = (char *)(array + cnt + 1); + + /* Copy strings. */ + memcpy(tptr, elems, nelems); + free(elems, M_OFWPROP); + + /* Fill string pointers. */ + for (i = 0, cnt = 0; i < nelems; cnt++) { + len = strlen(tptr) + 1; + array[cnt] = tptr; + i += len; + tptr += len; + } + array[cnt] = 0; + *out_array = array; + + return (cnt); +} diff --git a/freebsd/sys/dev/ofw/ofw_bus_subr.h b/freebsd/sys/dev/ofw/ofw_bus_subr.h new file mode 100644 index 00000000..30f299a6 --- /dev/null +++ b/freebsd/sys/dev/ofw/ofw_bus_subr.h @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org> + * 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, + * without modification, immediately at the beginning of the file. + * 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 _DEV_OFW_OFW_BUS_SUBR_H_ +#define _DEV_OFW_OFW_BUS_SUBR_H_ + +#include <sys/bus.h> +#ifdef INTRNG +#include <sys/intr.h> +#endif +#include <dev/ofw/openfirm.h> + +#include <rtems/bsd/local/ofw_bus_if.h> + +#define ORIP_NOINT -1 +#define ORIR_NOTFOUND 0xffffffff + +struct ofw_bus_iinfo { + uint8_t *opi_imap; + uint8_t *opi_imapmsk; + int opi_imapsz; + pcell_t opi_addrc; +}; + +struct ofw_compat_data { + const char *ocd_str; + uintptr_t ocd_data; +}; + +#ifdef INTRNG +struct intr_map_data_fdt { + struct intr_map_data hdr; + phandle_t iparent; + u_int ncells; + pcell_t cells[]; +}; +#endif + +#define SIMPLEBUS_PNP_DESCR "Z:compat;P:private;" +#define SIMPLEBUS_PNP_INFO(t) \ + MODULE_PNP_INFO(SIMPLEBUS_PNP_DESCR, simplebus, t, t, sizeof(t[0]), sizeof(t) / sizeof(t[0])); + +/* Generic implementation of ofw_bus_if.m methods and helper routines */ +int ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *, phandle_t); +void ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *); + +ofw_bus_get_compat_t ofw_bus_gen_get_compat; +ofw_bus_get_model_t ofw_bus_gen_get_model; +ofw_bus_get_name_t ofw_bus_gen_get_name; +ofw_bus_get_node_t ofw_bus_gen_get_node; +ofw_bus_get_type_t ofw_bus_gen_get_type; + +/* Helper method to report interesting OF properties in pnpinfo */ +bus_child_pnpinfo_str_t ofw_bus_gen_child_pnpinfo_str; + +/* Routines for processing firmware interrupt maps */ +void ofw_bus_setup_iinfo(phandle_t, struct ofw_bus_iinfo *, int); +int ofw_bus_lookup_imap(phandle_t, struct ofw_bus_iinfo *, void *, int, + void *, int, void *, int, phandle_t *); +int ofw_bus_search_intrmap(void *, int, void *, int, void *, int, void *, + void *, void *, int, phandle_t *); + +/* Routines for processing msi maps */ +int ofw_bus_msimap(phandle_t, uint16_t, phandle_t *, uint32_t *); + +/* Routines for parsing device-tree data into resource lists. */ +int ofw_bus_reg_to_rl(device_t, phandle_t, pcell_t, pcell_t, + struct resource_list *); +int ofw_bus_intr_to_rl(device_t, phandle_t, struct resource_list *, int *); +int ofw_bus_intr_by_rid(device_t, phandle_t, int, phandle_t *, int *, + pcell_t **); + +/* Helper to get device status property */ +const char *ofw_bus_get_status(device_t dev); +int ofw_bus_status_okay(device_t dev); + +/* Helper to get node's interrupt parent */ +phandle_t ofw_bus_find_iparent(phandle_t); + +/* Helper routine for checking compat prop */ +int ofw_bus_is_compatible(device_t, const char *); +int ofw_bus_is_compatible_strict(device_t, const char *); +int ofw_bus_node_is_compatible(phandle_t, const char *); + +/* + * Helper routine to search a list of compat properties. The table is + * terminated by an entry with a NULL compat-string pointer; a pointer to that + * table entry is returned if none of the compat strings match for the device, + * giving you control over the not-found value. Will not return NULL unless the + * provided table pointer is NULL. + */ +const struct ofw_compat_data * + ofw_bus_search_compatible(device_t, const struct ofw_compat_data *); + +/* Helper routine for checking existence of a prop */ +int ofw_bus_has_prop(device_t, const char *); + +/* Helper to search for a child with a given compat prop */ +phandle_t ofw_bus_find_compatible(phandle_t, const char *); + +/* Helper to search for a child with a given name */ +phandle_t ofw_bus_find_child(phandle_t, const char *); + +/* Helper routine to find a device_t child matching a given phandle_t */ +device_t ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node); + +/* Helper routines for parsing lists */ +int ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name, + const char *cells_name, int idx, phandle_t *producer, int *ncells, + pcell_t **cells); +int ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name, + const char *cells_name, int *count); +int ofw_bus_find_string_index(phandle_t node, const char *list_name, + const char *name, int *idx); +int ofw_bus_string_list_to_array(phandle_t node, const char *list_name, + const char ***array); + +#endif /* !_DEV_OFW_OFW_BUS_SUBR_H_ */ diff --git a/freebsd/sys/dev/ofw/ofw_fdt.c b/freebsd/sys/dev/ofw/ofw_fdt.c new file mode 100644 index 00000000..6bdaf89b --- /dev/null +++ b/freebsd/sys/dev/ofw/ofw_fdt.c @@ -0,0 +1,490 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <contrib/libfdt/libfdt.h> + +#include <machine/stdarg.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofwvar.h> +#include <dev/ofw/openfirm.h> + +#include <rtems/bsd/local/ofw_if.h> + +#ifdef DEBUG +#define debugf(fmt, args...) do { printf("%s(): ", __func__); \ + printf(fmt,##args); } while (0) +#else +#define debugf(fmt, args...) +#endif + +#if defined(__arm__) +#if defined(SOC_MV_ARMADAXP) || defined(SOC_MV_ARMADA38X) || \ + defined(SOC_MV_DISCOVERY) || defined(SOC_MV_DOVE) || \ + defined(SOC_MV_FREY) || defined(SOC_MV_KIRKWOOD) || \ + defined(SOC_MV_LOKIPLUS) || defined(SOC_MV_ORION) +#define FDT_MARVELL +#endif +#endif + +static int ofw_fdt_init(ofw_t, void *); +static phandle_t ofw_fdt_peer(ofw_t, phandle_t); +static phandle_t ofw_fdt_child(ofw_t, phandle_t); +static phandle_t ofw_fdt_parent(ofw_t, phandle_t); +static phandle_t ofw_fdt_instance_to_package(ofw_t, ihandle_t); +static ssize_t ofw_fdt_getproplen(ofw_t, phandle_t, const char *); +static ssize_t ofw_fdt_getprop(ofw_t, phandle_t, const char *, void *, size_t); +static int ofw_fdt_nextprop(ofw_t, phandle_t, const char *, char *, size_t); +static int ofw_fdt_setprop(ofw_t, phandle_t, const char *, const void *, + size_t); +static ssize_t ofw_fdt_canon(ofw_t, const char *, char *, size_t); +static phandle_t ofw_fdt_finddevice(ofw_t, const char *); +static ssize_t ofw_fdt_instance_to_path(ofw_t, ihandle_t, char *, size_t); +static ssize_t ofw_fdt_package_to_path(ofw_t, phandle_t, char *, size_t); +static int ofw_fdt_interpret(ofw_t, const char *, int, cell_t *); + +static ofw_method_t ofw_fdt_methods[] = { + OFWMETHOD(ofw_init, ofw_fdt_init), + OFWMETHOD(ofw_peer, ofw_fdt_peer), + OFWMETHOD(ofw_child, ofw_fdt_child), + OFWMETHOD(ofw_parent, ofw_fdt_parent), + OFWMETHOD(ofw_instance_to_package, ofw_fdt_instance_to_package), + OFWMETHOD(ofw_getproplen, ofw_fdt_getproplen), + OFWMETHOD(ofw_getprop, ofw_fdt_getprop), + OFWMETHOD(ofw_nextprop, ofw_fdt_nextprop), + OFWMETHOD(ofw_setprop, ofw_fdt_setprop), + OFWMETHOD(ofw_canon, ofw_fdt_canon), + OFWMETHOD(ofw_finddevice, ofw_fdt_finddevice), + OFWMETHOD(ofw_instance_to_path, ofw_fdt_instance_to_path), + OFWMETHOD(ofw_package_to_path, ofw_fdt_package_to_path), + OFWMETHOD(ofw_interpret, ofw_fdt_interpret), + { 0, 0 } +}; + +static ofw_def_t ofw_fdt = { + OFW_FDT, + ofw_fdt_methods, + 0 +}; +OFW_DEF(ofw_fdt); + +static void *fdtp = NULL; + +static int +sysctl_handle_dtb(SYSCTL_HANDLER_ARGS) +{ + + return (sysctl_handle_opaque(oidp, fdtp, fdt_totalsize(fdtp), req)); +} + +static void +sysctl_register_fdt_oid(void *arg) +{ + + /* If there is no FDT registered, skip adding the sysctl */ + if (fdtp == NULL) + return; + + SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw_fdt), OID_AUTO, "dtb", + CTLTYPE_OPAQUE | CTLFLAG_RD, NULL, 0, sysctl_handle_dtb, "", + "Device Tree Blob"); +} +SYSINIT(dtb_oid, SI_SUB_KMEM, SI_ORDER_ANY, sysctl_register_fdt_oid, 0); + +static int +ofw_fdt_init(ofw_t ofw, void *data) +{ + int err; + + /* Check FDT blob integrity */ + if ((err = fdt_check_header(data)) != 0) + return (err); + + fdtp = data; + return (0); +} + +/* + * Device tree functions. + * + * We use the offset from fdtp to the node as the 'phandle' in OF interface. + * + * phandle is a u32 value, therefore we cannot use the pointer to node as + * phandle in 64 bit. We also do not use the usual fdt offset as phandle, + * as it can be 0, and the OF interface has special meaning for phandle 0. + */ + +static phandle_t +fdt_offset_phandle(int offset) +{ + if (offset < 0) + return (0); + return ((phandle_t)offset + fdt_off_dt_struct(fdtp)); +} + +static int +fdt_phandle_offset(phandle_t p) +{ + int pint = (int)p; + int dtoff = fdt_off_dt_struct(fdtp); + + if (pint < dtoff) + return (-1); + return (pint - dtoff); +} + +/* Return the next sibling of this node or 0. */ +static phandle_t +ofw_fdt_peer(ofw_t ofw, phandle_t node) +{ + int depth, offset; + + if (node == 0) { + /* Find root node */ + offset = fdt_path_offset(fdtp, "/"); + + return (fdt_offset_phandle(offset)); + } + + offset = fdt_phandle_offset(node); + if (offset < 0) + return (0); + + for (depth = 1, offset = fdt_next_node(fdtp, offset, &depth); + offset >= 0; + offset = fdt_next_node(fdtp, offset, &depth)) { + if (depth < 0) + return (0); + if (depth == 1) + return (fdt_offset_phandle(offset)); + } + + return (0); +} + +/* Return the first child of this node or 0. */ +static phandle_t +ofw_fdt_child(ofw_t ofw, phandle_t node) +{ + int depth, offset; + + offset = fdt_phandle_offset(node); + if (offset < 0) + return (0); + + for (depth = 0, offset = fdt_next_node(fdtp, offset, &depth); + (offset >= 0) && (depth > 0); + offset = fdt_next_node(fdtp, offset, &depth)) { + if (depth < 0) + return (0); + if (depth == 1) + return (fdt_offset_phandle(offset)); + } + + return (0); +} + +/* Return the parent of this node or 0. */ +static phandle_t +ofw_fdt_parent(ofw_t ofw, phandle_t node) +{ + int offset, paroffset; + + offset = fdt_phandle_offset(node); + if (offset < 0) + return (0); + + paroffset = fdt_parent_offset(fdtp, offset); + return (fdt_offset_phandle(paroffset)); +} + +/* Return the package handle that corresponds to an instance handle. */ +static phandle_t +ofw_fdt_instance_to_package(ofw_t ofw, ihandle_t instance) +{ + + /* Where real OF uses ihandles in the tree, FDT uses xref phandles */ + return (OF_node_from_xref(instance)); +} + +/* Get the length of a property of a package. */ +static ssize_t +ofw_fdt_getproplen(ofw_t ofw, phandle_t package, const char *propname) +{ + const struct fdt_property *prop; + int offset, len; + + offset = fdt_phandle_offset(package); + if (offset < 0) + return (-1); + + len = -1; + prop = fdt_get_property(fdtp, offset, propname, &len); + + if (prop == NULL && strcmp(propname, "name") == 0) { + /* Emulate the 'name' property */ + fdt_get_name(fdtp, offset, &len); + return (len + 1); + } + + if (prop == NULL && offset == fdt_path_offset(fdtp, "/chosen")) { + if (strcmp(propname, "fdtbootcpu") == 0) + return (sizeof(cell_t)); + if (strcmp(propname, "fdtmemreserv") == 0) + return (sizeof(uint64_t)*2*fdt_num_mem_rsv(fdtp)); + } + + if (prop == NULL) + return (-1); + + return (len); +} + +/* Get the value of a property of a package. */ +static ssize_t +ofw_fdt_getprop(ofw_t ofw, phandle_t package, const char *propname, void *buf, + size_t buflen) +{ + const void *prop; + const char *name; + int len, offset; + uint32_t cpuid; + + offset = fdt_phandle_offset(package); + if (offset < 0) + return (-1); + + prop = fdt_getprop(fdtp, offset, propname, &len); + + if (prop == NULL && strcmp(propname, "name") == 0) { + /* Emulate the 'name' property */ + name = fdt_get_name(fdtp, offset, &len); + strncpy(buf, name, buflen); + if (len + 1 > buflen) + len = buflen; + return (len + 1); + } + + if (prop == NULL && offset == fdt_path_offset(fdtp, "/chosen")) { + 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); + + if (len > buflen) + len = buflen; + bcopy(prop, buf, len); + return (len); +} + +/* + * Get the next property of a package. Return values: + * -1: package or previous property does not exist + * 0: no more properties + * 1: success + */ +static int +ofw_fdt_nextprop(ofw_t ofw, phandle_t package, const char *previous, char *buf, + size_t size) +{ + const struct fdt_property *prop; + const char *name; + int offset; + + offset = fdt_phandle_offset(package); + if (offset < 0) + return (-1); + + /* Find the first prop in the node */ + offset = fdt_first_property_offset(fdtp, offset); + if (offset < 0) + return (0); /* No properties */ + + if (previous != NULL) { + while (offset >= 0) { + prop = fdt_get_property_by_offset(fdtp, offset, NULL); + if (prop == NULL) + return (-1); /* Internal error */ + + offset = fdt_next_property_offset(fdtp, offset); + if (offset < 0) + return (0); /* No more properties */ + + /* Check if the last one was the one we wanted */ + name = fdt_string(fdtp, fdt32_to_cpu(prop->nameoff)); + if (strcmp(name, previous) == 0) + break; + } + } + + prop = fdt_get_property_by_offset(fdtp, offset, &offset); + if (prop == NULL) + return (-1); /* Internal error */ + + strncpy(buf, fdt_string(fdtp, fdt32_to_cpu(prop->nameoff)), size); + + return (1); +} + +/* Set the value of a property of a package. */ +static int +ofw_fdt_setprop(ofw_t ofw, phandle_t package, const char *propname, + const void *buf, size_t len) +{ + int offset; + + offset = fdt_phandle_offset(package); + if (offset < 0) + return (-1); + + return (fdt_setprop_inplace(fdtp, offset, propname, buf, len)); +} + +/* Convert a device specifier to a fully qualified pathname. */ +static ssize_t +ofw_fdt_canon(ofw_t ofw, const char *device, char *buf, size_t len) +{ + + return (-1); +} + +/* Return a package handle for the specified device. */ +static phandle_t +ofw_fdt_finddevice(ofw_t ofw, const char *device) +{ + int offset; + + offset = fdt_path_offset(fdtp, device); + if (offset < 0) + return (-1); + return (fdt_offset_phandle(offset)); +} + +/* Return the fully qualified pathname corresponding to an instance. */ +static ssize_t +ofw_fdt_instance_to_path(ofw_t ofw, ihandle_t instance, char *buf, size_t len) +{ + phandle_t phandle; + + phandle = OF_instance_to_package(instance); + if (phandle == -1) + return (-1); + + return (OF_package_to_path(phandle, buf, len)); +} + +/* Return the fully qualified pathname corresponding to a package. */ +static ssize_t +ofw_fdt_package_to_path(ofw_t ofw, phandle_t package, char *buf, size_t len) +{ + + return (-1); +} + +#if defined(FDT_MARVELL) || defined(__powerpc__) +static int +ofw_fdt_fixup(ofw_t ofw) +{ +#define FDT_MODEL_LEN 80 + char model[FDT_MODEL_LEN]; + phandle_t root; + ssize_t len; + int i; + + if ((root = ofw_fdt_finddevice(ofw, "/")) == -1) + return (ENODEV); + + if ((len = ofw_fdt_getproplen(ofw, root, "model")) <= 0) + return (0); + + bzero(model, FDT_MODEL_LEN); + if (ofw_fdt_getprop(ofw, root, "model", model, FDT_MODEL_LEN) <= 0) + return (0); + + /* + * Search fixup table and call handler if appropriate. + */ + for (i = 0; fdt_fixup_table[i].model != NULL; i++) { + if (strncmp(model, fdt_fixup_table[i].model, + FDT_MODEL_LEN) != 0) + continue; + + if (fdt_fixup_table[i].handler != NULL) + (*fdt_fixup_table[i].handler)(root); + } + + return (0); +} +#endif + +static int +ofw_fdt_interpret(ofw_t ofw, const char *cmd, int nret, cell_t *retvals) +{ +#if defined(FDT_MARVELL) || defined(__powerpc__) + int rv; + + /* + * Note: FDT does not have the possibility to 'interpret' commands, + * but we abuse the interface a bit to use it for doing non-standard + * operations on the device tree blob. + * + * Currently the only supported 'command' is to trigger performing + * fixups. + */ + if (strncmp("perform-fixup", cmd, 13) != 0) + return (0); + + rv = ofw_fdt_fixup(ofw); + if (nret > 0) + retvals[0] = rv; + + return (rv); +#else + return (0); +#endif +} diff --git a/freebsd/sys/dev/ofw/ofw_pci.h b/freebsd/sys/dev/ofw/ofw_pci.h new file mode 100644 index 00000000..eb60c5ba --- /dev/null +++ b/freebsd/sys/dev/ofw/ofw_pci.h @@ -0,0 +1,103 @@ +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + * + * from: NetBSD: ofw_pci.h,v 1.5 2003/10/22 09:04:39 mjl Exp + * + * $FreeBSD$ + */ + +#ifndef _DEV_OFW_OFW_PCI_H_ +#define _DEV_OFW_OFW_PCI_H_ + +/* + * PCI Bus Binding to: + * + * IEEE Std 1275-1994 + * Standard for Boot (Initialization Configuration) Firmware + * + * Revision 2.1 + */ + +/* + * Section 2.2.1. Physical Address Formats + * + * A PCI physical address is represented by 3 address cells: + * + * phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr + * phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh + * phys.lo cell: llllllll llllllll llllllll llllllll + * + * n nonrelocatable + * p prefetchable + * t aliased below 1MB (memory) or 64k (i/o) + * ss space code + * b bus number + * d device number + * f function number + * r register number + * h high 32-bits of PCI address + * l low 32-bits of PCI address + */ + +#define OFW_PCI_PHYS_HI_NONRELOCATABLE 0x80000000 +#define OFW_PCI_PHYS_HI_PREFETCHABLE 0x40000000 +#define OFW_PCI_PHYS_HI_ALIASED 0x20000000 +#define OFW_PCI_PHYS_HI_SPACEMASK 0x03000000 +#define OFW_PCI_PHYS_HI_BUSMASK 0x00ff0000 +#define OFW_PCI_PHYS_HI_BUSSHIFT 16 +#define OFW_PCI_PHYS_HI_DEVICEMASK 0x0000f800 +#define OFW_PCI_PHYS_HI_DEVICESHIFT 11 +#define OFW_PCI_PHYS_HI_FUNCTIONMASK 0x00000700 +#define OFW_PCI_PHYS_HI_FUNCTIONSHIFT 8 +#define OFW_PCI_PHYS_HI_REGISTERMASK 0x000000ff + +#define OFW_PCI_PHYS_HI_SPACE_CONFIG 0x00000000 +#define OFW_PCI_PHYS_HI_SPACE_IO 0x01000000 +#define OFW_PCI_PHYS_HI_SPACE_MEM32 0x02000000 +#define OFW_PCI_PHYS_HI_SPACE_MEM64 0x03000000 + +#define OFW_PCI_PHYS_HI_BUS(hi) \ + (((hi) & OFW_PCI_PHYS_HI_BUSMASK) >> OFW_PCI_PHYS_HI_BUSSHIFT) +#define OFW_PCI_PHYS_HI_DEVICE(hi) \ + (((hi) & OFW_PCI_PHYS_HI_DEVICEMASK) >> OFW_PCI_PHYS_HI_DEVICESHIFT) +#define OFW_PCI_PHYS_HI_FUNCTION(hi) \ + (((hi) & OFW_PCI_PHYS_HI_FUNCTIONMASK) >> OFW_PCI_PHYS_HI_FUNCTIONSHIFT) + +/* + * This has the 3 32bit cell values, plus 2 more to make up a 64-bit size. + */ +struct ofw_pci_register { + u_int32_t phys_hi; + u_int32_t phys_mid; + u_int32_t phys_lo; + u_int32_t size_hi; + u_int32_t size_lo; +}; + +#endif /* _DEV_OFW_OFW_PCI_H_ */ diff --git a/freebsd/sys/dev/ofw/ofw_subr.c b/freebsd/sys/dev/ofw/ofw_subr.c new file mode 100644 index 00000000..391d25c4 --- /dev/null +++ b/freebsd/sys/dev/ofw/ofw_subr.c @@ -0,0 +1,246 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (c) 2015 Ian Lepore <ian@freebsd.org> + * 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. + * + * The initial ofw_reg_to_paddr() implementation has been copied from powerpc + * ofw_machdep.c OF_decode_addr(). It was added by Marcel Moolenaar, who did not + * assert copyright with the addition but still deserves credit for the work. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/bus.h> +#include <sys/libkern.h> +#include <sys/reboot.h> +#include <sys/rman.h> + +#include <machine/bus.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_pci.h> +#include <dev/ofw/ofw_subr.h> + +static void +get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep, int *pcip) +{ + char type[64]; + uint32_t addr, size; + int pci, res; + + res = OF_getencprop(node, "#address-cells", &addr, sizeof(addr)); + if (res == -1) + addr = 2; + res = OF_getencprop(node, "#size-cells", &size, sizeof(size)); + if (res == -1) + size = 1; + pci = 0; + if (addr == 3 && size == 2) { + res = OF_getprop(node, "device_type", type, sizeof(type)); + if (res != -1) { + type[sizeof(type) - 1] = '\0'; + pci = (strcmp(type, "pci") == 0) ? 1 : 0; + } + } + if (addrp != NULL) + *addrp = addr; + if (sizep != NULL) + *sizep = size; + if (pcip != NULL) + *pcip = pci; +} + +int +ofw_reg_to_paddr(phandle_t dev, int regno, bus_addr_t *paddr, + bus_size_t *psize, pcell_t *ppci_hi) +{ + pcell_t cell[32], pci_hi; + uint64_t addr, raddr, baddr; + uint64_t size, rsize; + uint32_t c, nbridge, naddr, nsize; + phandle_t bridge, parent; + u_int spc, rspc; + int pci, pcib, res; + + /* Sanity checking. */ + if (dev == 0) + return (EINVAL); + bridge = OF_parent(dev); + if (bridge == 0) + return (EINVAL); + if (regno < 0) + return (EINVAL); + if (paddr == NULL || psize == NULL) + return (EINVAL); + + get_addr_props(bridge, &naddr, &nsize, &pci); + res = OF_getencprop(dev, (pci) ? "assigned-addresses" : "reg", + cell, sizeof(cell)); + if (res == -1) + return (ENXIO); + if (res % sizeof(cell[0])) + return (ENXIO); + res /= sizeof(cell[0]); + regno *= naddr + nsize; + if (regno + naddr + nsize > res) + return (EINVAL); + pci_hi = pci ? cell[regno] : OFW_PADDR_NOT_PCI; + spc = pci_hi & OFW_PCI_PHYS_HI_SPACEMASK; + addr = 0; + for (c = 0; c < naddr; c++) + addr = ((uint64_t)addr << 32) | cell[regno++]; + size = 0; + for (c = 0; c < nsize; c++) + size = ((uint64_t)size << 32) | cell[regno++]; + /* + * Map the address range in the bridge's decoding window as given + * by the "ranges" property. If a node doesn't have such property + * or the property is empty, we assume an identity mapping. The + * standard says a missing property indicates no possible mapping. + * This code is more liberal since the intended use is to get a + * console running early, and a printf to warn of malformed data + * is probably futile before the console is fully set up. + */ + parent = OF_parent(bridge); + while (parent != 0) { + get_addr_props(parent, &nbridge, NULL, &pcib); + res = OF_getencprop(bridge, "ranges", cell, sizeof(cell)); + if (res < 1) + goto next; + if (res % sizeof(cell[0])) + return (ENXIO); + /* Capture pci_hi if we just transitioned onto a PCI bus. */ + if (pcib && pci_hi == OFW_PADDR_NOT_PCI) { + pci_hi = cell[0]; + spc = pci_hi & OFW_PCI_PHYS_HI_SPACEMASK; + } + res /= sizeof(cell[0]); + regno = 0; + while (regno < res) { + rspc = (pci ? cell[regno] : OFW_PADDR_NOT_PCI) & + OFW_PCI_PHYS_HI_SPACEMASK; + if (rspc != spc) { + regno += naddr + nbridge + nsize; + continue; + } + raddr = 0; + for (c = 0; c < naddr; c++) + raddr = ((uint64_t)raddr << 32) | cell[regno++]; + rspc = (pcib) + ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK + : OFW_PADDR_NOT_PCI; + baddr = 0; + for (c = 0; c < nbridge; c++) + baddr = ((uint64_t)baddr << 32) | cell[regno++]; + rsize = 0; + for (c = 0; c < nsize; c++) + rsize = ((uint64_t)rsize << 32) | cell[regno++]; + if (addr < raddr || addr >= raddr + rsize) + continue; + addr = addr - raddr + baddr; + if (rspc != OFW_PADDR_NOT_PCI) + spc = rspc; + } + next: + bridge = parent; + parent = OF_parent(bridge); + get_addr_props(bridge, &naddr, &nsize, &pci); + } + + KASSERT(addr <= BUS_SPACE_MAXADDR, + ("Bus sddress is too large: %jx", (uintmax_t)addr)); + KASSERT(size <= BUS_SPACE_MAXSIZE, + ("Bus size is too large: %jx", (uintmax_t)size)); + + *paddr = addr; + *psize = size; + if (ppci_hi != NULL) + *ppci_hi = pci_hi; + + return (0); +} + +/* Parse cmd line args as env - copied from xlp_machdep. */ +/* XXX-BZ this should really be centrally provided for all (boot) code. */ +static void +_parse_bootargs(char *cmdline) +{ + char *n, *v; + + while ((v = strsep(&cmdline, " \n")) != NULL) { + if (*v == '\0') + continue; + if (*v == '-') { + while (*v != '\0') { + v++; + switch (*v) { + case 'a': boothowto |= RB_ASKNAME; break; + /* Someone should simulate that ;-) */ + case 'C': boothowto |= RB_CDROM; break; + case 'd': boothowto |= RB_KDB; break; + case 'D': boothowto |= RB_MULTIPLE; break; + case 'm': boothowto |= RB_MUTE; break; + case 'g': boothowto |= RB_GDB; break; + case 'h': boothowto |= RB_SERIAL; break; + case 'p': boothowto |= RB_PAUSE; break; + case 'r': boothowto |= RB_DFLTROOT; break; + case 's': boothowto |= RB_SINGLE; break; + case 'v': boothowto |= RB_VERBOSE; break; + } + } + } else { + n = strsep(&v, "="); + if (v == NULL) + kern_setenv(n, "1"); + else + kern_setenv(n, v); + } + } +} + +/* + * This is intended to be called early on, right after the OF system is + * initialized, so pmap may not be up yet. + */ +int +ofw_parse_bootargs(void) +{ + phandle_t chosen; + char buf[2048]; /* early stack supposedly big enough */ + int err; + + chosen = OF_finddevice("/chosen"); + if (chosen <= 0) + return (chosen); + + if ((err = OF_getprop(chosen, "bootargs", buf, sizeof(buf))) != -1) { + _parse_bootargs(buf); + return (0); + } + + return (err); +} diff --git a/freebsd/sys/dev/ofw/ofw_subr.h b/freebsd/sys/dev/ofw/ofw_subr.h new file mode 100644 index 00000000..ed29142d --- /dev/null +++ b/freebsd/sys/dev/ofw/ofw_subr.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 2015 Ian Lepore <ian@freebsd.org> + * 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 _DEV_OFW_OFW_SUBR_H_ +#define _DEV_OFW_OFW_SUBR_H_ + +/* + * Translate an address from the Nth tuple of a device node's reg properties to + * a physical memory address, by applying the range mappings from all ancestors. + * This assumes that all ancestor ranges are simple numerical offsets for which + * addition and subtraction operations will perform the required mapping (the + * bit-options in the high word of standard PCI properties are also handled). + * After the call, *pci_hi (if non-NULL) contains the phys.hi cell of the + * device's parent PCI bus, or OFW_PADDR_NOT_PCI if no PCI bus is involved. + * + * This is intended to be a helper function called by the platform-specific + * implementation of OF_decode_addr(), and not for direct use by device drivers. + */ +#define OFW_PADDR_NOT_PCI (~0) + +int ofw_reg_to_paddr(phandle_t _dev, int _regno, bus_addr_t *_paddr, + bus_size_t *_size, pcell_t *_pci_hi); + +int ofw_parse_bootargs(void); + +#endif diff --git a/freebsd/sys/dev/ofw/ofwbus.c b/freebsd/sys/dev/ofw/ofwbus.c new file mode 100644 index 00000000..58ec2841 --- /dev/null +++ b/freebsd/sys/dev/ofw/ofwbus.c @@ -0,0 +1,297 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright 1998 Massachusetts Institute of Technology + * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. + * Copyright 2006 by Marius Strobl <marius@FreeBSD.org>. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. 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. + * + * from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/pcpu.h> +#include <sys/rman.h> +#ifdef INTRNG +#include <sys/intr.h> +#endif + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> +#include <dev/fdt/simplebus.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +/* + * The ofwbus (which is a pseudo-bus actually) iterates over the nodes that + * hang from the Open Firmware root node and adds them as devices to this bus + * (except some special nodes which are excluded) so that drivers can be + * attached to them. + * + */ + +struct ofwbus_softc { + struct simplebus_softc simplebus_sc; + struct rman sc_intr_rman; + struct rman sc_mem_rman; +}; + +#ifndef __aarch64__ +static device_identify_t ofwbus_identify; +#endif +static device_probe_t ofwbus_probe; +static device_attach_t ofwbus_attach; +static bus_alloc_resource_t ofwbus_alloc_resource; +static bus_adjust_resource_t ofwbus_adjust_resource; +static bus_release_resource_t ofwbus_release_resource; + +static device_method_t ofwbus_methods[] = { + /* Device interface */ +#ifndef __aarch64__ + DEVMETHOD(device_identify, ofwbus_identify), +#endif + DEVMETHOD(device_probe, ofwbus_probe), + DEVMETHOD(device_attach, ofwbus_attach), + + /* Bus interface */ + DEVMETHOD(bus_alloc_resource, ofwbus_alloc_resource), + DEVMETHOD(bus_adjust_resource, ofwbus_adjust_resource), + DEVMETHOD(bus_release_resource, ofwbus_release_resource), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(ofwbus, ofwbus_driver, ofwbus_methods, + sizeof(struct ofwbus_softc), simplebus_driver); +static devclass_t ofwbus_devclass; +EARLY_DRIVER_MODULE(ofwbus, nexus, ofwbus_driver, ofwbus_devclass, 0, 0, + BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(ofwbus, 1); + +#ifndef __aarch64__ +static void +ofwbus_identify(driver_t *driver, device_t parent) +{ + + /* Check if Open Firmware has been instantiated */ + if (OF_peer(0) == 0) + return; + + if (device_find_child(parent, "ofwbus", -1) == NULL) + BUS_ADD_CHILD(parent, 0, "ofwbus", -1); +} +#endif + +static int +ofwbus_probe(device_t dev) +{ + +#ifdef __aarch64__ + if (OF_peer(0) == 0) + return (ENXIO); +#endif + + device_set_desc(dev, "Open Firmware Device Tree"); + return (BUS_PROBE_NOWILDCARD); +} + +static int +ofwbus_attach(device_t dev) +{ + struct ofwbus_softc *sc; + phandle_t node; + struct ofw_bus_devinfo obd; + + sc = device_get_softc(dev); + + node = OF_peer(0); + + /* + * If no Open Firmware, bail early + */ + if (node == -1) + return (ENXIO); + + /* + * ofwbus bus starts on unamed node in FDT, so we cannot make + * ofw_bus_devinfo from it. Pass node to simplebus_init directly. + */ + simplebus_init(dev, node); + sc->sc_intr_rman.rm_type = RMAN_ARRAY; + sc->sc_intr_rman.rm_descr = "Interrupts"; + sc->sc_mem_rman.rm_type = RMAN_ARRAY; + sc->sc_mem_rman.rm_descr = "Device Memory"; + if (rman_init(&sc->sc_intr_rman) != 0 || + rman_init(&sc->sc_mem_rman) != 0 || + rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0 || + rman_manage_region(&sc->sc_mem_rman, 0, BUS_SPACE_MAXADDR) != 0) + panic("%s: failed to set up rmans.", __func__); + + /* + * Allow devices to identify. + */ + bus_generic_probe(dev); + + /* + * Now walk the OFW tree and attach top-level devices. + */ + for (node = OF_child(node); node > 0; node = OF_peer(node)) { + if (ofw_bus_gen_setup_devinfo(&obd, node) != 0) + continue; + simplebus_add_device(dev, node, 0, NULL, -1, NULL); + } + return (bus_generic_attach(dev)); +} + +static struct resource * +ofwbus_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 ofwbus_softc *sc; + struct rman *rm; + struct resource *rv; + struct resource_list_entry *rle; + int isdefault, passthrough; + + isdefault = RMAN_IS_DEFAULT_RANGE(start, end); + passthrough = (device_get_parent(child) != bus); + sc = device_get_softc(bus); + rle = NULL; + if (!passthrough && isdefault) { + rle = resource_list_find(BUS_GET_RESOURCE_LIST(bus, child), + 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; + count = ummax(count, rle->count); + end = ummax(rle->end, start + count - 1); + } + + switch (type) { + case SYS_RES_IRQ: + rm = &sc->sc_intr_rman; + break; + case SYS_RES_MEMORY: + rm = &sc->sc_mem_rman; + break; + default: + return (NULL); + } + + rv = rman_reserve_resource(rm, start, end, count, flags & ~RF_ACTIVE, + child); + if (rv == NULL) + return (NULL); + rman_set_rid(rv, *rid); + + if ((flags & RF_ACTIVE) != 0 && bus_activate_resource(child, type, + *rid, rv) != 0) { + rman_release_resource(rv); + return (NULL); + } + + if (!passthrough && rle != NULL) { + rle->res = rv; + rle->start = rman_get_start(rv); + rle->end = rman_get_end(rv); + rle->count = rle->end - rle->start + 1; + } + + return (rv); +} + +static int +ofwbus_adjust_resource(device_t bus, device_t child __unused, int type, + struct resource *r, rman_res_t start, rman_res_t end) +{ + struct ofwbus_softc *sc; + struct rman *rm; + device_t ofwbus; + + ofwbus = bus; + while (strcmp(device_get_name(device_get_parent(ofwbus)), "root") != 0) + ofwbus = device_get_parent(ofwbus); + sc = device_get_softc(ofwbus); + switch (type) { + case SYS_RES_IRQ: + rm = &sc->sc_intr_rman; + break; + case SYS_RES_MEMORY: + rm = &sc->sc_mem_rman; + break; + default: + return (EINVAL); + } + if (rm == NULL) + return (ENXIO); + if (rman_is_region_manager(r, rm) == 0) + return (EINVAL); + return (rman_adjust_resource(r, start, end)); +} + +static int +ofwbus_release_resource(device_t bus, device_t child, int type, + int rid, struct resource *r) +{ + struct resource_list_entry *rle; + int passthrough; + int error; + + passthrough = (device_get_parent(child) != bus); + if (!passthrough) { + /* Clean resource list entry */ + rle = resource_list_find(BUS_GET_RESOURCE_LIST(bus, child), + type, rid); + if (rle != NULL) + rle->res = NULL; + } + + if ((rman_get_flags(r) & RF_ACTIVE) != 0) { + error = bus_deactivate_resource(child, type, rid, r); + if (error) + return (error); + } + return (rman_release_resource(r)); +} diff --git a/freebsd/sys/dev/ofw/ofwvar.h b/freebsd/sys/dev/ofw/ofwvar.h new file mode 100644 index 00000000..ddaa3db0 --- /dev/null +++ b/freebsd/sys/dev/ofw/ofwvar.h @@ -0,0 +1,89 @@ +/*- + * Copyright (c) 2005 Peter Grehan + * Copyright (c) 2008 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 _DEV_OFW_OFWVAR_H_ +#define _DEV_OFW_OFWVAR_H_ + +/* + * An Open Firmware client implementation is declared with a kernel object and + * an associated method table, similar to a device driver. + * + * e.g. + * + * static ofw_method_t fdt_methods[] = { + * OFWMETHOD(ofw_init, fdt_init), + * OFWMETHOD(ofw_finddevice, fdt_finddevice), + * ... + * OFWMETHOD(ofw_nextprop, fdt_nextprop), + * { 0, 0 } + * }; + * + * static ofw_def_t ofw_fdt = { + * "ofw_fdt", + * fdt_methods, + * sizeof(fdt_softc), // or 0 if no softc + * }; + * + * OFW_DEF(ofw_fdt); + */ + +#include <sys/kobj.h> + +struct ofw_kobj { + /* + * An OFW instance is a kernel object. + */ + KOBJ_FIELDS; + + /* + * Utility elements that an instance may use + */ + struct mtx ofw_mtx; /* available for instance use */ + void *ofw_iptr; /* instance data pointer */ + + /* + * Opaque data that can be overlaid with an instance-private + * structure. OFW code can test that this is large enough at + * compile time with a sizeof() test againt it's softc. There + * is also a run-time test when the MMU kernel object is + * registered. + */ +#define OFW_OPAQUESZ 64 + u_int ofw_opaque[OFW_OPAQUESZ]; +}; + +typedef struct ofw_kobj *ofw_t; +typedef struct kobj_class ofw_def_t; + +#define ofw_method_t kobj_method_t +#define OFWMETHOD KOBJMETHOD + +#define OFW_DEF(name) DATA_SET(ofw_set, name) + +#endif /* _DEV_OFW_OFWVAR_H_ */ diff --git a/freebsd/sys/dev/ofw/openfirm.c b/freebsd/sys/dev/ofw/openfirm.c new file mode 100644 index 00000000..6c6cd874 --- /dev/null +++ b/freebsd/sys/dev/ofw/openfirm.c @@ -0,0 +1,807 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/* $NetBSD: Locore.c,v 1.7 2000/08/20 07:04:59 tsubai Exp $ */ + +/*- + * Copyright (C) 1995, 1996 Wolfgang Solfrank. + * Copyright (C) 1995, 1996 TooLs GmbH. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by TooLs GmbH. + * 4. The name of TooLs GmbH may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. + */ +/*- + * Copyright (C) 2000 Benno Rice. + * 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 Benno Rice ``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 TOOLS GMBH 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/local/opt_platform.h> + +#include <rtems/bsd/sys/param.h> +#include <sys/kernel.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/queue.h> +#include <sys/systm.h> +#include <sys/endian.h> + +#include <machine/stdarg.h> + +#include <dev/ofw/ofwvar.h> +#include <dev/ofw/openfirm.h> + +#include <rtems/bsd/local/ofw_if.h> + +static void OF_putchar(int c, void *arg); + +MALLOC_DEFINE(M_OFWPROP, "openfirm", "Open Firmware properties"); + +static ihandle_t stdout; + +static ofw_def_t *ofw_def_impl = NULL; +static ofw_t ofw_obj; +static struct ofw_kobj ofw_kernel_obj; +static struct kobj_ops ofw_kernel_kops; + +struct xrefinfo { + phandle_t xref; + phandle_t node; + device_t dev; + SLIST_ENTRY(xrefinfo) next_entry; +}; + +static SLIST_HEAD(, xrefinfo) xreflist = SLIST_HEAD_INITIALIZER(xreflist); +static struct mtx xreflist_lock; +static boolean_t xref_init_done; + +#define FIND_BY_XREF 0 +#define FIND_BY_NODE 1 +#define FIND_BY_DEV 2 + +/* + * xref-phandle-device lookup helper routines. + * + * As soon as we are able to use malloc(), walk the node tree and build a list + * of info that cross-references node handles, xref handles, and device_t + * instances. This list exists primarily to allow association of a device_t + * with an xref handle, but it is also used to speed up translation between xref + * and node handles. Before malloc() is available we have to recursively search + * the node tree each time we want to translate between a node and xref handle. + * Afterwards we can do the translations by searching this much shorter list. + */ +static void +xrefinfo_create(phandle_t node) +{ + struct xrefinfo * xi; + phandle_t child, xref; + + /* + * Recursively descend from parent, looking for nodes with a property + * named either "phandle", "ibm,phandle", or "linux,phandle". For each + * such node found create an entry in the xreflist. + */ + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + xrefinfo_create(child); + if (OF_getencprop(child, "phandle", &xref, sizeof(xref)) == + -1 && OF_getencprop(child, "ibm,phandle", &xref, + sizeof(xref)) == -1 && OF_getencprop(child, + "linux,phandle", &xref, sizeof(xref)) == -1) + continue; + xi = malloc(sizeof(*xi), M_OFWPROP, M_WAITOK | M_ZERO); + xi->node = child; + xi->xref = xref; + SLIST_INSERT_HEAD(&xreflist, xi, next_entry); + } +} + +static void +xrefinfo_init(void *unsed) +{ + + /* + * There is no locking during this init because it runs much earlier + * than any of the clients/consumers of the xref list data, but we do + * initialize the mutex that will be used for access later. + */ + mtx_init(&xreflist_lock, "OF xreflist lock", NULL, MTX_DEF); + xrefinfo_create(OF_peer(0)); + xref_init_done = true; +} +SYSINIT(xrefinfo, SI_SUB_KMEM, SI_ORDER_ANY, xrefinfo_init, NULL); + +static struct xrefinfo * +xrefinfo_find(uintptr_t key, int find_by) +{ + struct xrefinfo *rv, *xi; + + rv = NULL; + mtx_lock(&xreflist_lock); + SLIST_FOREACH(xi, &xreflist, next_entry) { + if ((find_by == FIND_BY_XREF && (phandle_t)key == xi->xref) || + (find_by == FIND_BY_NODE && (phandle_t)key == xi->node) || + (find_by == FIND_BY_DEV && key == (uintptr_t)xi->dev)) { + rv = xi; + break; + } + } + mtx_unlock(&xreflist_lock); + return (rv); +} + +static struct xrefinfo * +xrefinfo_add(phandle_t node, phandle_t xref, device_t dev) +{ + struct xrefinfo *xi; + + xi = malloc(sizeof(*xi), M_OFWPROP, M_WAITOK); + xi->node = node; + xi->xref = xref; + xi->dev = dev; + mtx_lock(&xreflist_lock); + SLIST_INSERT_HEAD(&xreflist, xi, next_entry); + mtx_unlock(&xreflist_lock); + return (xi); +} + +/* + * OFW install routines. Highest priority wins, equal priority also + * overrides allowing last-set to win. + */ +SET_DECLARE(ofw_set, ofw_def_t); + +boolean_t +OF_install(char *name, int prio) +{ + ofw_def_t *ofwp, **ofwpp; + static int curr_prio = 0; + + /* + * Try and locate the OFW kobj corresponding to the name. + */ + SET_FOREACH(ofwpp, ofw_set) { + ofwp = *ofwpp; + + if (ofwp->name && + !strcmp(ofwp->name, name) && + prio >= curr_prio) { + curr_prio = prio; + ofw_def_impl = ofwp; + return (TRUE); + } + } + + return (FALSE); +} + +/* Initializer */ +int +OF_init(void *cookie) +{ + phandle_t chosen; + int rv; + + if (ofw_def_impl == NULL) + return (-1); + + ofw_obj = &ofw_kernel_obj; + /* + * Take care of compiling the selected class, and + * then statically initialize the OFW object. + */ + kobj_class_compile_static(ofw_def_impl, &ofw_kernel_kops); + kobj_init_static((kobj_t)ofw_obj, ofw_def_impl); + + rv = OFW_INIT(ofw_obj, cookie); + + if ((chosen = OF_finddevice("/chosen")) != -1) + if (OF_getencprop(chosen, "stdout", &stdout, + sizeof(stdout)) == -1) + stdout = -1; + + return (rv); +} + +static void +OF_putchar(int c, void *arg __unused) +{ + char cbuf; + + if (c == '\n') { + cbuf = '\r'; + OF_write(stdout, &cbuf, 1); + } + + cbuf = c; + OF_write(stdout, &cbuf, 1); +} + +void +OF_printf(const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + (void)kvprintf(fmt, OF_putchar, NULL, 10, va); + va_end(va); +} + +/* + * Generic functions + */ + +/* Test to see if a service exists. */ +int +OF_test(const char *name) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_TEST(ofw_obj, name)); +} + +int +OF_interpret(const char *cmd, int nreturns, ...) +{ + va_list ap; + cell_t slots[16]; + int i = 0; + int status; + + if (ofw_def_impl == NULL) + return (-1); + + status = OFW_INTERPRET(ofw_obj, cmd, nreturns, slots); + if (status == -1) + return (status); + + va_start(ap, nreturns); + while (i < nreturns) + *va_arg(ap, cell_t *) = slots[i++]; + va_end(ap); + + return (status); +} + +/* + * Device tree functions + */ + +/* Return the next sibling of this node or 0. */ +phandle_t +OF_peer(phandle_t node) +{ + + if (ofw_def_impl == NULL) + return (0); + + return (OFW_PEER(ofw_obj, node)); +} + +/* Return the first child of this node or 0. */ +phandle_t +OF_child(phandle_t node) +{ + + if (ofw_def_impl == NULL) + return (0); + + return (OFW_CHILD(ofw_obj, node)); +} + +/* Return the parent of this node or 0. */ +phandle_t +OF_parent(phandle_t node) +{ + + if (ofw_def_impl == NULL) + return (0); + + return (OFW_PARENT(ofw_obj, node)); +} + +/* Return the package handle that corresponds to an instance handle. */ +phandle_t +OF_instance_to_package(ihandle_t instance) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_INSTANCE_TO_PACKAGE(ofw_obj, instance)); +} + +/* Get the length of a property of a package. */ +ssize_t +OF_getproplen(phandle_t package, const char *propname) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_GETPROPLEN(ofw_obj, package, propname)); +} + +/* Check existence of a property of a package. */ +int +OF_hasprop(phandle_t package, const char *propname) +{ + + return (OF_getproplen(package, propname) >= 0 ? 1 : 0); +} + +/* Get the value of a property of a package. */ +ssize_t +OF_getprop(phandle_t package, const char *propname, void *buf, size_t buflen) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_GETPROP(ofw_obj, package, propname, buf, buflen)); +} + +ssize_t +OF_getencprop(phandle_t node, const char *propname, pcell_t *buf, size_t len) +{ + ssize_t retval; + int i; + + KASSERT(len % 4 == 0, ("Need a multiple of 4 bytes")); + + retval = OF_getprop(node, propname, buf, len); + if (retval <= 0) + return (retval); + + for (i = 0; i < len/4; i++) + buf[i] = be32toh(buf[i]); + + return (retval); +} + +/* + * Recursively search the node and its parent for the given property, working + * downward from the node to the device tree root. Returns the value of the + * first match. + */ +ssize_t +OF_searchprop(phandle_t node, const char *propname, void *buf, size_t len) +{ + ssize_t rv; + + for (; node != 0; node = OF_parent(node)) + if ((rv = OF_getprop(node, propname, buf, len)) != -1) + return (rv); + return (-1); +} + +ssize_t +OF_searchencprop(phandle_t node, const char *propname, void *buf, size_t len) +{ + ssize_t rv; + + for (; node != 0; node = OF_parent(node)) + if ((rv = OF_getencprop(node, propname, buf, len)) != -1) + return (rv); + return (-1); +} + +/* + * Store the value of a property of a package into newly allocated memory + * (using the M_OFWPROP malloc pool and M_WAITOK). elsz is the size of a + * single element, the number of elements is return in number. + */ +ssize_t +OF_getprop_alloc(phandle_t package, const char *propname, int elsz, void **buf) +{ + int len; + + *buf = NULL; + if ((len = OF_getproplen(package, propname)) == -1 || + len % elsz != 0) + return (-1); + + *buf = malloc(len, M_OFWPROP, M_WAITOK); + if (OF_getprop(package, propname, *buf, len) == -1) { + free(*buf, M_OFWPROP); + *buf = NULL; + return (-1); + } + return (len / elsz); +} + +ssize_t +OF_getencprop_alloc(phandle_t package, const char *name, int elsz, void **buf) +{ + ssize_t retval; + pcell_t *cell; + int i; + + retval = OF_getprop_alloc(package, name, elsz, buf); + if (retval == -1) + return (-1); + if (retval * elsz % 4 != 0) { + free(*buf, M_OFWPROP); + *buf = NULL; + return (-1); + } + + cell = *buf; + for (i = 0; i < retval * elsz / 4; i++) + cell[i] = be32toh(cell[i]); + + return (retval); +} + +/* Free buffer allocated by OF_getencprop_alloc or OF_getprop_alloc */ +void OF_prop_free(void *buf) +{ + + free(buf, M_OFWPROP); +} + +/* Get the next property of a package. */ +int +OF_nextprop(phandle_t package, const char *previous, char *buf, size_t size) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_NEXTPROP(ofw_obj, package, previous, buf, size)); +} + +/* Set the value of a property of a package. */ +int +OF_setprop(phandle_t package, const char *propname, const void *buf, size_t len) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_SETPROP(ofw_obj, package, propname, buf,len)); +} + +/* Convert a device specifier to a fully qualified pathname. */ +ssize_t +OF_canon(const char *device, char *buf, size_t len) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_CANON(ofw_obj, device, buf, len)); +} + +/* Return a package handle for the specified device. */ +phandle_t +OF_finddevice(const char *device) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_FINDDEVICE(ofw_obj, device)); +} + +/* Return the fully qualified pathname corresponding to an instance. */ +ssize_t +OF_instance_to_path(ihandle_t instance, char *buf, size_t len) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_INSTANCE_TO_PATH(ofw_obj, instance, buf, len)); +} + +/* Return the fully qualified pathname corresponding to a package. */ +ssize_t +OF_package_to_path(phandle_t package, char *buf, size_t len) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_PACKAGE_TO_PATH(ofw_obj, package, buf, len)); +} + +/* Look up effective phandle (see FDT/PAPR spec) */ +static phandle_t +OF_child_xref_phandle(phandle_t parent, phandle_t xref) +{ + phandle_t child, rxref; + + /* + * Recursively descend from parent, looking for a node with a property + * named either "phandle", "ibm,phandle", or "linux,phandle" that + * matches the xref we are looking for. + */ + + for (child = OF_child(parent); child != 0; child = OF_peer(child)) { + rxref = OF_child_xref_phandle(child, xref); + if (rxref != -1) + return (rxref); + + if (OF_getencprop(child, "phandle", &rxref, sizeof(rxref)) == + -1 && OF_getencprop(child, "ibm,phandle", &rxref, + sizeof(rxref)) == -1 && OF_getencprop(child, + "linux,phandle", &rxref, sizeof(rxref)) == -1) + continue; + + if (rxref == xref) + return (child); + } + + return (-1); +} + +phandle_t +OF_node_from_xref(phandle_t xref) +{ + struct xrefinfo *xi; + phandle_t node; + + if (xref_init_done) { + if ((xi = xrefinfo_find(xref, FIND_BY_XREF)) == NULL) + return (xref); + return (xi->node); + } + + if ((node = OF_child_xref_phandle(OF_peer(0), xref)) == -1) + return (xref); + return (node); +} + +phandle_t +OF_xref_from_node(phandle_t node) +{ + struct xrefinfo *xi; + phandle_t xref; + + if (xref_init_done) { + if ((xi = xrefinfo_find(node, FIND_BY_NODE)) == NULL) + return (node); + return (xi->xref); + } + + if (OF_getencprop(node, "phandle", &xref, sizeof(xref)) == -1 && + OF_getencprop(node, "ibm,phandle", &xref, sizeof(xref)) == -1 && + OF_getencprop(node, "linux,phandle", &xref, sizeof(xref)) == -1) + return (node); + return (xref); +} + +device_t +OF_device_from_xref(phandle_t xref) +{ + struct xrefinfo *xi; + + if (xref_init_done) { + if ((xi = xrefinfo_find(xref, FIND_BY_XREF)) == NULL) + return (NULL); + return (xi->dev); + } + panic("Attempt to find device before xreflist_init"); +} + +phandle_t +OF_xref_from_device(device_t dev) +{ + struct xrefinfo *xi; + + if (xref_init_done) { + if ((xi = xrefinfo_find((uintptr_t)dev, FIND_BY_DEV)) == NULL) + return (0); + return (xi->xref); + } + panic("Attempt to find xref before xreflist_init"); +} + +int +OF_device_register_xref(phandle_t xref, device_t dev) +{ + struct xrefinfo *xi; + + /* + * If the given xref handle doesn't already exist in the list then we + * add a list entry. In theory this can only happen on a system where + * nodes don't contain phandle properties and xref and node handles are + * synonymous, so the xref handle is added as the node handle as well. + */ + if (xref_init_done) { + if ((xi = xrefinfo_find(xref, FIND_BY_XREF)) == NULL) + xrefinfo_add(xref, xref, dev); + else + xi->dev = dev; + return (0); + } + panic("Attempt to register device before xreflist_init"); +} + +/* Call the method in the scope of a given instance. */ +int +OF_call_method(const char *method, ihandle_t instance, int nargs, int nreturns, + ...) +{ + va_list ap; + cell_t args_n_results[12]; + int n, status; + + if (nargs > 6 || ofw_def_impl == NULL) + return (-1); + va_start(ap, nreturns); + for (n = 0; n < nargs; n++) + args_n_results[n] = va_arg(ap, cell_t); + + status = OFW_CALL_METHOD(ofw_obj, instance, method, nargs, nreturns, + args_n_results); + if (status != 0) + return (status); + + for (; n < nargs + nreturns; n++) + *va_arg(ap, cell_t *) = args_n_results[n]; + va_end(ap); + return (0); +} + +/* + * Device I/O functions + */ + +/* Open an instance for a device. */ +ihandle_t +OF_open(const char *device) +{ + + if (ofw_def_impl == NULL) + return (0); + + return (OFW_OPEN(ofw_obj, device)); +} + +/* Close an instance. */ +void +OF_close(ihandle_t instance) +{ + + if (ofw_def_impl == NULL) + return; + + OFW_CLOSE(ofw_obj, instance); +} + +/* Read from an instance. */ +ssize_t +OF_read(ihandle_t instance, void *addr, size_t len) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_READ(ofw_obj, instance, addr, len)); +} + +/* Write to an instance. */ +ssize_t +OF_write(ihandle_t instance, const void *addr, size_t len) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_WRITE(ofw_obj, instance, addr, len)); +} + +/* Seek to a position. */ +int +OF_seek(ihandle_t instance, uint64_t pos) +{ + + if (ofw_def_impl == NULL) + return (-1); + + return (OFW_SEEK(ofw_obj, instance, pos)); +} + +/* + * Memory functions + */ + +/* Claim an area of memory. */ +void * +OF_claim(void *virt, size_t size, u_int align) +{ + + if (ofw_def_impl == NULL) + return ((void *)-1); + + return (OFW_CLAIM(ofw_obj, virt, size, align)); +} + +/* Release an area of memory. */ +void +OF_release(void *virt, size_t size) +{ + + if (ofw_def_impl == NULL) + return; + + OFW_RELEASE(ofw_obj, virt, size); +} + +/* + * Control transfer functions + */ + +/* Suspend and drop back to the Open Firmware interface. */ +void +OF_enter() +{ + + if (ofw_def_impl == NULL) + return; + + OFW_ENTER(ofw_obj); +} + +/* Shut down and drop back to the Open Firmware interface. */ +void +OF_exit() +{ + + if (ofw_def_impl == NULL) + panic("OF_exit: Open Firmware not available"); + + /* Should not return */ + OFW_EXIT(ofw_obj); + + for (;;) /* just in case */ + ; +} diff --git a/freebsd/sys/dev/tsec/if_tsec_fdt.c b/freebsd/sys/dev/tsec/if_tsec_fdt.c new file mode 100644 index 00000000..1ae47b9c --- /dev/null +++ b/freebsd/sys/dev/tsec/if_tsec_fdt.c @@ -0,0 +1,390 @@ +#include <machine/rtems-bsd-kernel-space.h> + +/*- + * Copyright (C) 2007-2008 Semihalf, Rafal Jaworowski + * Copyright (C) 2006-2007 Semihalf, Piotr Kruszynski + * 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 ``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 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. + * + * From: FreeBSD: head/sys/dev/tsec/if_tsec_ocp.c 188712 2009-02-17 14:59:47Z raj + */ + +/* + * FDT 'simple-bus' attachment for Freescale TSEC controller. + */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <rtems/bsd/sys/param.h> +#include <sys/systm.h> +#include <sys/endian.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/mbuf.h> +#include <sys/mutex.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sysctl.h> + +#include <sys/bus.h> +#include <machine/bus.h> +#include <sys/rman.h> +#include <machine/resource.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_media.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> + +#include <dev/tsec/if_tsec.h> +#include <dev/tsec/if_tsecreg.h> + +#include <rtems/bsd/local/miibus_if.h> + +#define TSEC_RID_TXIRQ 0 +#define TSEC_RID_RXIRQ 1 +#define TSEC_RID_ERRIRQ 2 + +static int tsec_fdt_probe(device_t dev); +static int tsec_fdt_attach(device_t dev); +static int tsec_fdt_detach(device_t dev); +static int tsec_setup_intr(struct tsec_softc *sc, struct resource **ires, + void **ihand, int *irid, driver_intr_t handler, const char *iname); +static void tsec_release_intr(struct tsec_softc *sc, struct resource *ires, + void *ihand, int irid, const char *iname); + +static device_method_t tsec_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, tsec_fdt_probe), + DEVMETHOD(device_attach, tsec_fdt_attach), + DEVMETHOD(device_detach, tsec_fdt_detach), + + DEVMETHOD(device_shutdown, tsec_shutdown), + DEVMETHOD(device_suspend, tsec_suspend), + DEVMETHOD(device_resume, tsec_resume), + + /* MII interface */ + DEVMETHOD(miibus_readreg, tsec_miibus_readreg), + DEVMETHOD(miibus_writereg, tsec_miibus_writereg), + DEVMETHOD(miibus_statchg, tsec_miibus_statchg), + + DEVMETHOD_END +}; + +static driver_t tsec_fdt_driver = { + "tsec", + tsec_methods, + sizeof(struct tsec_softc), +}; + +DRIVER_MODULE(tsec, simplebus, tsec_fdt_driver, tsec_devclass, 0, 0); + +static int +tsec_fdt_probe(device_t dev) +{ + struct tsec_softc *sc; + uint32_t id; + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_get_type(dev) == NULL || + strcmp(ofw_bus_get_type(dev), "network") != 0) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "gianfar") && + !ofw_bus_is_compatible(dev, "fsl,etsec2")) + return (ENXIO); + + sc = device_get_softc(dev); + + /* + * Device trees with "fsl,etsec2" compatible nodes don't have a reg + * property, as it's been relegated to the queue-group children. + */ + if (ofw_bus_is_compatible(dev, "fsl,etsec2")) + sc->is_etsec = 1; + else { + sc->sc_rrid = 0; + sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rrid, + RF_ACTIVE); + if (sc->sc_rres == NULL) + return (ENXIO); + + sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); + sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); + + /* Check if we are eTSEC (enhanced TSEC) */ + id = TSEC_READ(sc, TSEC_REG_ID); + sc->is_etsec = ((id >> 16) == TSEC_ETSEC_ID) ? 1 : 0; + id |= TSEC_READ(sc, TSEC_REG_ID2); + + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rrid, sc->sc_rres); + + if (id == 0) { + device_printf(dev, "could not identify TSEC type\n"); + return (ENXIO); + } + } + + if (sc->is_etsec) + device_set_desc(dev, "Enhanced Three-Speed Ethernet Controller"); + else + device_set_desc(dev, "Three-Speed Ethernet Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +tsec_fdt_attach(device_t dev) +{ + struct tsec_softc *sc; + struct resource_list *rl; + phandle_t child, mdio, phy; + int acells, scells; + int error = 0; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->node = ofw_bus_get_node(dev); + + if (fdt_addrsize_cells(sc->node, &acells, &scells) != 0) { + acells = 1; + scells = 1; + } + if (ofw_bus_is_compatible(dev, "fsl,etsec2")) { + rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); + + /* + * TODO: Add all children resources to the list. Will be + * required to support multigroup mode. + */ + child = OF_child(sc->node); + ofw_bus_reg_to_rl(dev, child, acells, scells, rl); + ofw_bus_intr_to_rl(dev, child, rl, NULL); + } + + /* Get phy address from fdt */ + if (OF_getencprop(sc->node, "phy-handle", &phy, sizeof(phy)) <= 0) { + device_printf(dev, "PHY not found in device tree"); + return (ENXIO); + } + + phy = OF_node_from_xref(phy); + mdio = OF_parent(phy); + OF_decode_addr(mdio, 0, &sc->phy_bst, &sc->phy_bsh, NULL); + OF_getencprop(phy, "reg", &sc->phyaddr, sizeof(sc->phyaddr)); + + /* + * etsec2 MDIO nodes are given the MDIO module base address, so we need + * to add the MII offset to get the PHY registers. + */ + if (ofw_bus_node_is_compatible(mdio, "fsl,etsec2-mdio")) + sc->phy_regoff = TSEC_REG_MIIBASE; + + /* Init timer */ + callout_init(&sc->tsec_callout, 1); + + /* Init locks */ + mtx_init(&sc->transmit_lock, device_get_nameunit(dev), "TSEC TX lock", + MTX_DEF); + mtx_init(&sc->receive_lock, device_get_nameunit(dev), "TSEC RX lock", + MTX_DEF); + mtx_init(&sc->ic_lock, device_get_nameunit(dev), "TSEC IC lock", + MTX_DEF); + + /* Allocate IO memory for TSEC registers */ + sc->sc_rrid = 0; + sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rrid, + RF_ACTIVE); + if (sc->sc_rres == NULL) { + device_printf(dev, "could not allocate IO memory range!\n"); + goto fail1; + } + sc->sc_bas.bsh = rman_get_bushandle(sc->sc_rres); + sc->sc_bas.bst = rman_get_bustag(sc->sc_rres); + + /* TSEC attach */ + if (tsec_attach(sc) != 0) { + device_printf(dev, "could not be configured\n"); + goto fail2; + } + + /* Set up interrupts (TX/RX/ERR) */ + sc->sc_transmit_irid = TSEC_RID_TXIRQ; + error = tsec_setup_intr(sc, &sc->sc_transmit_ires, + &sc->sc_transmit_ihand, &sc->sc_transmit_irid, + tsec_transmit_intr, "TX"); + if (error) + goto fail2; + + sc->sc_receive_irid = TSEC_RID_RXIRQ; + error = tsec_setup_intr(sc, &sc->sc_receive_ires, + &sc->sc_receive_ihand, &sc->sc_receive_irid, + tsec_receive_intr, "RX"); + if (error) + goto fail3; + + sc->sc_error_irid = TSEC_RID_ERRIRQ; + error = tsec_setup_intr(sc, &sc->sc_error_ires, + &sc->sc_error_ihand, &sc->sc_error_irid, + tsec_error_intr, "ERR"); + if (error) + goto fail4; + + return (0); + +fail4: + tsec_release_intr(sc, sc->sc_receive_ires, sc->sc_receive_ihand, + sc->sc_receive_irid, "RX"); +fail3: + tsec_release_intr(sc, sc->sc_transmit_ires, sc->sc_transmit_ihand, + sc->sc_transmit_irid, "TX"); +fail2: + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rrid, sc->sc_rres); +fail1: + mtx_destroy(&sc->receive_lock); + mtx_destroy(&sc->transmit_lock); + return (ENXIO); +} + +static int +tsec_setup_intr(struct tsec_softc *sc, struct resource **ires, void **ihand, + int *irid, driver_intr_t handler, const char *iname) +{ + int error; + + *ires = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, irid, RF_ACTIVE); + if (*ires == NULL) { + device_printf(sc->dev, "could not allocate %s IRQ\n", iname); + return (ENXIO); + } + error = bus_setup_intr(sc->dev, *ires, INTR_TYPE_NET | INTR_MPSAFE, + NULL, handler, sc, ihand); + if (error) { + device_printf(sc->dev, "failed to set up %s IRQ\n", iname); + if (bus_release_resource(sc->dev, SYS_RES_IRQ, *irid, *ires)) + device_printf(sc->dev, "could not release %s IRQ\n", iname); + *ires = NULL; + return (error); + } + return (0); +} + +static void +tsec_release_intr(struct tsec_softc *sc, struct resource *ires, void *ihand, + int irid, const char *iname) +{ + int error; + + if (ires == NULL) + return; + + error = bus_teardown_intr(sc->dev, ires, ihand); + if (error) + device_printf(sc->dev, "bus_teardown_intr() failed for %s intr" + ", error %d\n", iname, error); + + error = bus_release_resource(sc->dev, SYS_RES_IRQ, irid, ires); + if (error) + device_printf(sc->dev, "bus_release_resource() failed for %s " + "intr, error %d\n", iname, error); +} + +static int +tsec_fdt_detach(device_t dev) +{ + struct tsec_softc *sc; + int error; + + sc = device_get_softc(dev); + + /* Wait for stopping watchdog */ + callout_drain(&sc->tsec_callout); + + /* Stop and release all interrupts */ + tsec_release_intr(sc, sc->sc_transmit_ires, sc->sc_transmit_ihand, + sc->sc_transmit_irid, "TX"); + tsec_release_intr(sc, sc->sc_receive_ires, sc->sc_receive_ihand, + sc->sc_receive_irid, "RX"); + tsec_release_intr(sc, sc->sc_error_ires, sc->sc_error_ihand, + sc->sc_error_irid, "ERR"); + + /* TSEC detach */ + tsec_detach(sc); + + /* Free IO memory handler */ + if (sc->sc_rres) { + error = bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rrid, + sc->sc_rres); + if (error) + device_printf(dev, "bus_release_resource() failed for" + " IO memory, error %d\n", error); + } + + /* Destroy locks */ + mtx_destroy(&sc->receive_lock); + mtx_destroy(&sc->transmit_lock); + mtx_destroy(&sc->ic_lock); + return (0); +} + +void +tsec_get_hwaddr(struct tsec_softc *sc, uint8_t *addr) +{ + union { + uint32_t reg[2]; + uint8_t addr[6]; + } hw; + int i; + + hw.reg[0] = hw.reg[1] = 0; + + /* Retrieve the hardware address from the device tree. */ + i = OF_getprop(sc->node, "local-mac-address", (void *)hw.addr, 6); + if (i == 6 && (hw.reg[0] != 0 || hw.reg[1] != 0)) { + bcopy(hw.addr, addr, 6); + return; + } + + /* Also try the mac-address property, which is second-best */ + i = OF_getprop(sc->node, "mac-address", (void *)hw.addr, 6); + if (i == 6 && (hw.reg[0] != 0 || hw.reg[1] != 0)) { + bcopy(hw.addr, addr, 6); + return; + } + + /* + * Fall back -- use the currently programmed address in the hope that + * it was set be firmware... + */ + hw.reg[0] = TSEC_READ(sc, TSEC_REG_MACSTNADDR1); + hw.reg[1] = TSEC_READ(sc, TSEC_REG_MACSTNADDR2); + for (i = 0; i < 6; i++) + addr[5-i] = hw.addr[i]; +} diff --git a/freebsd/sys/sys/slicer.h b/freebsd/sys/sys/slicer.h new file mode 100644 index 00000000..9bf8748f --- /dev/null +++ b/freebsd/sys/sys/slicer.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2012 Semihalf. + * 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 _FLASH_SLICER_H_ +#define _FLASH_SLICER_H_ + +#include <sys/types.h> + +#define FLASH_SLICES_MAX_NUM 8 +#define FLASH_SLICES_MAX_NAME_LEN (32 + 1) + +#define FLASH_SLICES_FLAG_NONE 0 +#define FLASH_SLICES_FLAG_RO 1 /* Read only */ + +struct flash_slice { + off_t base; + off_t size; + char *label; + unsigned int flags; +}; + +#ifdef _KERNEL +int fdt_flash_fill_slices(device_t, struct flash_slice *, int *) __weak_symbol; +void flash_register_slicer(int (*)(device_t, struct flash_slice *, int *)); +#endif /* _KERNEL */ + +#endif /* _FLASH_SLICER_H_ */ |