FDT(4): Import from FreeBSD

This commit is contained in:
Sebastian Huber
2017-03-02 16:28:14 +01:00
parent 517e229e82
commit f0dd0c506a
16 changed files with 5048 additions and 0 deletions

View File

@@ -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", &reg, sizeof(reg));
if (len <= 0)
return (EINVAL);
*base = fdt_data_get(&reg[0], addr_cells);
*size = fdt_data_get(&reg[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 **)&reg);
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 *)&reg;
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);
}

View File

@@ -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_ */

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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_ */

View File

@@ -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 **)&reg);
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);
}

View File

@@ -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_ */

View File

@@ -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
}

View File

@@ -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_ */

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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_ */

View File

@@ -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 */
;
}

View File

@@ -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];
}

52
freebsd/sys/sys/slicer.h Normal file
View File

@@ -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_ */