mirror of
https://git.rtems.org/rtems-libbsd/
synced 2025-10-14 04:59:59 +08:00
FDT(4): Import from FreeBSD
This commit is contained in:
744
freebsd/sys/dev/fdt/fdt_common.c
Normal file
744
freebsd/sys/dev/fdt/fdt_common.c
Normal 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", ®, sizeof(reg));
|
||||
if (len <= 0)
|
||||
return (EINVAL);
|
||||
|
||||
*base = fdt_data_get(®[0], addr_cells);
|
||||
*size = fdt_data_get(®[addr_cells], size_cells);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
fdt_reg_to_rl(phandle_t node, struct resource_list *rl)
|
||||
{
|
||||
u_long end, count, start;
|
||||
pcell_t *reg, *regptr;
|
||||
pcell_t addr_cells, size_cells;
|
||||
int tuple_size, tuples;
|
||||
int i, rv;
|
||||
long busaddr, bussize;
|
||||
|
||||
if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0)
|
||||
return (ENXIO);
|
||||
if (fdt_get_range(OF_parent(node), 0, &busaddr, &bussize)) {
|
||||
busaddr = 0;
|
||||
bussize = 0;
|
||||
}
|
||||
|
||||
tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
|
||||
tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)®);
|
||||
debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells);
|
||||
debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size);
|
||||
if (tuples <= 0)
|
||||
/* No 'reg' property in this node. */
|
||||
return (0);
|
||||
|
||||
regptr = reg;
|
||||
for (i = 0; i < tuples; i++) {
|
||||
|
||||
rv = fdt_data_to_res(reg, addr_cells, size_cells, &start,
|
||||
&count);
|
||||
if (rv != 0) {
|
||||
resource_list_free(rl);
|
||||
goto out;
|
||||
}
|
||||
reg += addr_cells + size_cells;
|
||||
|
||||
/* Calculate address range relative to base. */
|
||||
start += busaddr;
|
||||
end = start + count - 1;
|
||||
|
||||
debugf("reg addr start = %lx, end = %lx, count = %lx\n", start,
|
||||
end, count);
|
||||
|
||||
resource_list_add(rl, SYS_RES_MEMORY, i, start, end,
|
||||
count);
|
||||
}
|
||||
rv = 0;
|
||||
|
||||
out:
|
||||
OF_prop_free(regptr);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
fdt_get_phyaddr(phandle_t node, device_t dev, int *phy_addr, void **phy_sc)
|
||||
{
|
||||
phandle_t phy_node;
|
||||
pcell_t phy_handle, phy_reg;
|
||||
uint32_t i;
|
||||
device_t parent, child;
|
||||
|
||||
if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
|
||||
sizeof(phy_handle)) <= 0)
|
||||
return (ENXIO);
|
||||
|
||||
phy_node = OF_node_from_xref(phy_handle);
|
||||
|
||||
if (OF_getencprop(phy_node, "reg", (void *)&phy_reg,
|
||||
sizeof(phy_reg)) <= 0)
|
||||
return (ENXIO);
|
||||
|
||||
*phy_addr = phy_reg;
|
||||
|
||||
/*
|
||||
* Search for softc used to communicate with phy.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Step 1: Search for ancestor of the phy-node with a "phy-handle"
|
||||
* property set.
|
||||
*/
|
||||
phy_node = OF_parent(phy_node);
|
||||
while (phy_node != 0) {
|
||||
if (OF_getprop(phy_node, "phy-handle", (void *)&phy_handle,
|
||||
sizeof(phy_handle)) > 0)
|
||||
break;
|
||||
phy_node = OF_parent(phy_node);
|
||||
}
|
||||
if (phy_node == 0)
|
||||
return (ENXIO);
|
||||
|
||||
/*
|
||||
* Step 2: For each device with the same parent and name as ours
|
||||
* compare its node with the one found in step 1, ancestor of phy
|
||||
* node (stored in phy_node).
|
||||
*/
|
||||
parent = device_get_parent(dev);
|
||||
i = 0;
|
||||
child = device_find_child(parent, device_get_name(dev), i);
|
||||
while (child != NULL) {
|
||||
if (ofw_bus_get_node(child) == phy_node)
|
||||
break;
|
||||
i++;
|
||||
child = device_find_child(parent, device_get_name(dev), i);
|
||||
}
|
||||
if (child == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
/*
|
||||
* Use softc of the device found.
|
||||
*/
|
||||
*phy_sc = (void *)device_get_softc(child);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
fdt_get_reserved_regions(struct mem_region *mr, int *mrcnt)
|
||||
{
|
||||
pcell_t reserve[FDT_REG_CELLS * FDT_MEM_REGIONS];
|
||||
pcell_t *reservep;
|
||||
phandle_t memory, root;
|
||||
uint32_t memory_size;
|
||||
int addr_cells, size_cells;
|
||||
int i, max_size, res_len, rv, tuple_size, tuples;
|
||||
|
||||
max_size = sizeof(reserve);
|
||||
root = OF_finddevice("/");
|
||||
memory = OF_finddevice("/memory");
|
||||
if (memory == -1) {
|
||||
rv = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
|
||||
&size_cells)) != 0)
|
||||
goto out;
|
||||
|
||||
if (addr_cells > 2) {
|
||||
rv = ERANGE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
|
||||
|
||||
res_len = OF_getproplen(root, "memreserve");
|
||||
if (res_len <= 0 || res_len > sizeof(reserve)) {
|
||||
rv = ERANGE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (OF_getprop(root, "memreserve", reserve, res_len) <= 0) {
|
||||
rv = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memory_size = 0;
|
||||
tuples = res_len / tuple_size;
|
||||
reservep = (pcell_t *)&reserve;
|
||||
for (i = 0; i < tuples; i++) {
|
||||
|
||||
rv = fdt_data_to_res(reservep, addr_cells, size_cells,
|
||||
(u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size);
|
||||
|
||||
if (rv != 0)
|
||||
goto out;
|
||||
|
||||
reservep += addr_cells + size_cells;
|
||||
}
|
||||
|
||||
*mrcnt = i;
|
||||
rv = 0;
|
||||
out:
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
fdt_get_mem_regions(struct mem_region *mr, int *mrcnt, uint64_t *memsize)
|
||||
{
|
||||
pcell_t reg[FDT_REG_CELLS * FDT_MEM_REGIONS];
|
||||
pcell_t *regp;
|
||||
phandle_t memory;
|
||||
uint64_t memory_size;
|
||||
int addr_cells, size_cells;
|
||||
int i, max_size, reg_len, rv, tuple_size, tuples;
|
||||
|
||||
max_size = sizeof(reg);
|
||||
memory = OF_finddevice("/memory");
|
||||
if (memory == -1) {
|
||||
rv = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
|
||||
&size_cells)) != 0)
|
||||
goto out;
|
||||
|
||||
if (addr_cells > 2) {
|
||||
rv = ERANGE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
|
||||
reg_len = OF_getproplen(memory, "reg");
|
||||
if (reg_len <= 0 || reg_len > sizeof(reg)) {
|
||||
rv = ERANGE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (OF_getprop(memory, "reg", reg, reg_len) <= 0) {
|
||||
rv = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memory_size = 0;
|
||||
tuples = reg_len / tuple_size;
|
||||
regp = (pcell_t *)®
|
||||
for (i = 0; i < tuples; i++) {
|
||||
|
||||
rv = fdt_data_to_res(regp, addr_cells, size_cells,
|
||||
(u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size);
|
||||
|
||||
if (rv != 0)
|
||||
goto out;
|
||||
|
||||
regp += addr_cells + size_cells;
|
||||
memory_size += mr[i].mr_size;
|
||||
}
|
||||
|
||||
if (memory_size == 0) {
|
||||
rv = ERANGE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*mrcnt = i;
|
||||
if (memsize != NULL)
|
||||
*memsize = memory_size;
|
||||
rv = 0;
|
||||
out:
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
fdt_get_unit(device_t dev)
|
||||
{
|
||||
const char * name;
|
||||
|
||||
name = ofw_bus_get_name(dev);
|
||||
name = strchr(name, '@') + 1;
|
||||
|
||||
return (strtol(name,NULL,0));
|
||||
}
|
||||
|
||||
int
|
||||
fdt_get_chosen_bootargs(char *bootargs, size_t max_size)
|
||||
{
|
||||
phandle_t chosen;
|
||||
|
||||
chosen = OF_finddevice("/chosen");
|
||||
if (chosen == -1)
|
||||
return (ENXIO);
|
||||
if (OF_getprop(chosen, "bootargs", bootargs, max_size) == -1)
|
||||
return (ENXIO);
|
||||
return (0);
|
||||
}
|
107
freebsd/sys/dev/fdt/fdt_common.h
Normal file
107
freebsd/sys/dev/fdt/fdt_common.h
Normal 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_ */
|
431
freebsd/sys/dev/fdt/simplebus.c
Normal file
431
freebsd/sys/dev/fdt/simplebus.c
Normal 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);
|
||||
}
|
64
freebsd/sys/dev/fdt/simplebus.h
Normal file
64
freebsd/sys/dev/fdt/simplebus.h
Normal 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 */
|
79
freebsd/sys/dev/ofw/ofw_bus.h
Normal file
79
freebsd/sys/dev/ofw/ofw_bus.h
Normal 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_ */
|
953
freebsd/sys/dev/ofw/ofw_bus_subr.c
Normal file
953
freebsd/sys/dev/ofw/ofw_bus_subr.c
Normal 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 **)®);
|
||||
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);
|
||||
}
|
145
freebsd/sys/dev/ofw/ofw_bus_subr.h
Normal file
145
freebsd/sys/dev/ofw/ofw_bus_subr.h
Normal 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_ */
|
490
freebsd/sys/dev/ofw/ofw_fdt.c
Normal file
490
freebsd/sys/dev/ofw/ofw_fdt.c
Normal 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
|
||||
}
|
103
freebsd/sys/dev/ofw/ofw_pci.h
Normal file
103
freebsd/sys/dev/ofw/ofw_pci.h
Normal 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_ */
|
246
freebsd/sys/dev/ofw/ofw_subr.c
Normal file
246
freebsd/sys/dev/ofw/ofw_subr.c
Normal 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);
|
||||
}
|
51
freebsd/sys/dev/ofw/ofw_subr.h
Normal file
51
freebsd/sys/dev/ofw/ofw_subr.h
Normal 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
|
297
freebsd/sys/dev/ofw/ofwbus.c
Normal file
297
freebsd/sys/dev/ofw/ofwbus.c
Normal 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));
|
||||
}
|
89
freebsd/sys/dev/ofw/ofwvar.h
Normal file
89
freebsd/sys/dev/ofw/ofwvar.h
Normal 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_ */
|
807
freebsd/sys/dev/ofw/openfirm.c
Normal file
807
freebsd/sys/dev/ofw/openfirm.c
Normal 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 */
|
||||
;
|
||||
}
|
390
freebsd/sys/dev/tsec/if_tsec_fdt.c
Normal file
390
freebsd/sys/dev/tsec/if_tsec_fdt.c
Normal 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
52
freebsd/sys/sys/slicer.h
Normal 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_ */
|
Reference in New Issue
Block a user