Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 2 Jun 2010 17:17:45 +0000 (UTC)
From:      Rafal Jaworowski <raj@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r208747 - head/sys/dev/fdt
Message-ID:  <201006021717.o52HHjfW017538@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: raj
Date: Wed Jun  2 17:17:45 2010
New Revision: 208747
URL: http://svn.freebsd.org/changeset/base/208747

Log:
  Import the common Flattened Device Tree infrastructure.
  
  o fdtbus(4) - the main abstract bus driver for all FDT-compliant systems. This
    is a direct replacement for the many incompatible bus drivers grouping
    integrated peripherals on embedded platforms (like obio(4), ocpbus(4) etc.)
  
  o simplebus(4) - bus driver representing ePAPR style 'simple-bus' node, which
    is an umbrella device for most of the integrated peripherals on a typical
    system-on-chip device.
  
  o Other components (common routines library, PCI node processing helper
    functions)
  
  Reviewed by:	imp
  Sponsored by:	The FreeBSD Foundation

Added:
  head/sys/dev/fdt/
  head/sys/dev/fdt/fdt_common.c   (contents, props changed)
  head/sys/dev/fdt/fdt_common.h   (contents, props changed)
  head/sys/dev/fdt/fdt_pci.c   (contents, props changed)
  head/sys/dev/fdt/fdt_static_dtb.S   (contents, props changed)
  head/sys/dev/fdt/fdtbus.c   (contents, props changed)
  head/sys/dev/fdt/simplebus.c   (contents, props changed)

Added: head/sys/dev/fdt/fdt_common.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/fdt/fdt_common.c	Wed Jun  2 17:17:45 2010	(r208747)
@@ -0,0 +1,629 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+
+#include <machine/fdt.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 "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
+
+vm_paddr_t fdt_immr_pa;
+vm_offset_t fdt_immr_va;
+vm_offset_t fdt_immr_size;
+
+int
+fdt_immr_addr(void)
+{
+	pcell_t ranges[6], *rangesptr;
+	phandle_t node;
+	u_long base, size;
+	pcell_t addr_cells, size_cells, par_addr_cells;
+	int len, tuple_size, tuples;
+
+	/*
+	 * Try to access the SOC node directly i.e. through /aliases/.
+	 */
+	if ((node = OF_finddevice("soc")) != 0)
+		if (fdt_is_compatible_strict(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", 1)) == 0)
+		return (ENXIO);
+
+moveon:
+	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 (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 (fdt_ranges_verify(ranges, tuples, par_addr_cells,
+	    addr_cells, size_cells)) {
+		return (ERANGE);
+	}
+	base = 0;
+	size = 0;
+	rangesptr = &ranges[0];
+
+	base = fdt_data_get((void *)rangesptr, addr_cells);
+	rangesptr += addr_cells;
+	base += fdt_data_get((void *)rangesptr, par_addr_cells);
+	rangesptr += par_addr_cells;
+	size = fdt_data_get((void *)rangesptr, size_cells);
+
+	fdt_immr_pa = base;
+	fdt_immr_va = FDT_IMMR_VA;
+	fdt_immr_size = size;
+
+	return (0);
+}
+
+/*
+ * 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.
+ */
+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);
+}
+
+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;
+
+	free(stat, M_OFWPROP);
+	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)
+		addr_cells = 2;
+
+	return ((int)fdt32_to_cpu(addr_cells));
+}
+
+int
+fdt_data_verify(void *data, int cells)
+{
+	uint64_t d64;
+
+	if (cells > 1) {
+		d64 = fdt64_to_cpu(*((uint64_t *)data));
+		if (((d64 >> 32) & 0xffffffffull) != 0 || cells > 2)
+			return (ERANGE);
+	}
+
+	return (0);
+}
+
+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_getprop(node, "#address-cells", &cell, cell_size) < cell_size)
+		cell = 2;
+	*addr_cells = fdt32_to_cpu((int)cell);
+
+	if (OF_getprop(node, "#size-cells", &cell, cell_size) < cell_size)
+		cell = 1;
+	*size_cells = fdt32_to_cpu((int)cell);
+
+	if (*addr_cells > 3 || *size_cells > 2)
+		return (ERANGE);
+	return (0);
+}
+
+int
+fdt_ranges_verify(pcell_t *ranges, int tuples, int par_addr_cells,
+    int this_addr_cells, int this_size_cells)
+{
+	int i, rv, ulsz;
+
+	if (par_addr_cells > 2 || this_addr_cells > 2 || this_size_cells > 2)
+		return (ERANGE);
+
+	/*
+	 * This is the max size the resource manager can handle for addresses
+	 * and sizes.
+	 */
+	ulsz = sizeof(u_long);
+	if (par_addr_cells <= ulsz && this_addr_cells <= ulsz &&
+	    this_size_cells <= ulsz)
+		/* We can handle everything */
+		return (0);
+
+	rv = 0;
+	for (i = 0; i < tuples; i++) {
+
+		if (fdt_data_verify((void *)ranges, par_addr_cells))
+			goto err;
+		ranges += par_addr_cells;
+
+		if (fdt_data_verify((void *)ranges, this_addr_cells))
+			goto err;
+		ranges += this_addr_cells;
+
+		if (fdt_data_verify((void *)ranges, this_size_cells))
+			goto err;
+		ranges += this_size_cells;
+	}
+
+	return (0);
+
+err:
+	debugf("using address range >%d-bit not supported\n", ulsz * 8);
+	return (ERANGE);
+}
+
+int
+fdt_data_to_res(pcell_t *data, int addr_cells, int size_cells, u_long *start,
+    u_long *count)
+{
+
+	/* Address portion. */
+	if (fdt_data_verify((void *)data, addr_cells))
+		return (ERANGE);
+
+	*start = fdt_data_get((void *)data, addr_cells);
+	data += addr_cells;
+
+	/* Size portion. */
+	if (fdt_data_verify((void *)data, size_cells))
+		return (ERANGE);
+
+	*count = fdt_data_get((void *)data, size_cells);
+	return (0);
+}
+
+int
+fdt_regsize(phandle_t node, u_long *base, u_long *size)
+{
+	pcell_t reg[4];
+	int addr_cells, len, size_cells;
+
+	if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells))
+		return (ENXIO);
+
+	if ((sizeof(pcell_t) * (addr_cells + size_cells)) > sizeof(reg))
+		return (ENOMEM);
+
+	len = OF_getprop(node, "reg", &reg, sizeof(reg));
+	if (len <= 0)
+		return (EINVAL);
+
+	*base = fdt_data_get(&reg[0], addr_cells);
+	*size = fdt_data_get(&reg[addr_cells], size_cells);
+	return (0);
+}
+
+int
+fdt_reg_to_rl(phandle_t node, struct resource_list *rl, u_long base)
+{
+	u_long start, end, count;
+	pcell_t *reg, *regptr;
+	pcell_t addr_cells, size_cells;
+	int tuple_size, tuples;
+	int i, rv;
+
+	if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0)
+		return (ENXIO);
+
+	tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
+	tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)&reg);
+	debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells);
+	debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size);
+	if (tuples <= 0)
+		/* No 'reg' property in this node. */
+		return (0);
+
+	regptr = reg;
+	for (i = 0; i < tuples; i++) {
+
+		rv = fdt_data_to_res(reg, addr_cells, size_cells, &start,
+		    &count);
+		if (rv != 0) {
+			resource_list_free(rl);
+			goto out;
+		}
+		reg += addr_cells + size_cells;
+
+		/* Calculate address range relative to base. */
+		start &= 0x000ffffful;
+		start = base + start;
+		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:
+	free(regptr, M_OFWPROP);
+	return (rv);
+}
+
+int
+fdt_intr_decode(phandle_t intr_parent, pcell_t *intr, int *interrupt,
+    int *trig, int *pol)
+{
+	fdt_pic_decode_t intr_decode;
+	int i, rv;
+
+	for (i = 0; fdt_pic_table[i] != NULL; i++) {
+
+		/* XXX check if pic_handle has interrupt-controller prop? */
+
+		intr_decode = fdt_pic_table[i];
+		rv = intr_decode(intr_parent, intr, interrupt, trig, pol);
+
+		if (rv == 0)
+			/* This was recognized as our PIC and decoded. */
+			return (0);
+	}
+
+	return (ENXIO);
+}
+
+int
+fdt_intr_to_rl(phandle_t node, struct resource_list *rl,
+    struct fdt_sense_level *intr_sl)
+{
+	phandle_t intr_par;
+	ihandle_t iph;
+	pcell_t *intr;
+	pcell_t intr_cells;
+	int interrupt, trig, pol;
+	int i, intr_num, rv;
+
+	if (OF_getproplen(node, "interrupts") <= 0)
+		/* Node does not have 'interrupts' property. */
+		return (0);
+
+	/*
+	 * Find #interrupt-cells of the interrupt domain.
+	 */
+	if (OF_getprop(node, "interrupt-parent", &iph, sizeof(iph)) <= 0) {
+		debugf("no intr-parent phandle\n");
+		intr_par = OF_parent(node);
+	} else {
+		iph = fdt32_to_cpu(iph);
+		intr_par = OF_instance_to_package(iph);
+	}
+
+	if (OF_getprop(intr_par, "#interrupt-cells", &intr_cells,
+	    sizeof(intr_cells)) <= 0) {
+		debugf("no intr-cells defined, defaulting to 1\n");
+		intr_cells = 1;
+	}
+	intr_cells = fdt32_to_cpu(intr_cells);
+
+	intr_num = OF_getprop_alloc(node, "interrupts",
+	    intr_cells * sizeof(pcell_t), (void **)&intr);
+	if (intr_num <= 0 || intr_num > DI_MAX_INTR_NUM)
+		return (ERANGE);
+
+	rv = 0;
+	for (i = 0; i < intr_num; i++) {
+
+		interrupt = -1;
+		trig = pol = 0;
+
+		if (fdt_intr_decode(intr_par, &intr[i * intr_cells],
+		    &interrupt, &trig, &pol) != 0) {
+			rv = ENXIO;
+			goto out;
+		}
+
+		if (interrupt < 0) {
+			rv = ERANGE;
+			goto out;
+		}
+
+		debugf("decoded intr = %d, trig = %d, pol = %d\n", interrupt,
+		    trig, pol);
+
+		intr_sl[intr_num].trig = trig;
+		intr_sl[intr_num].pol = pol;
+
+		resource_list_add(rl, SYS_RES_IRQ, i, interrupt, interrupt, 1);
+	}
+
+out:
+	free(intr, M_OFWPROP);
+	return (rv);
+}
+
+int
+fdt_get_phyaddr(phandle_t node, int *phy_addr)
+{
+	phandle_t phy_node;
+	ihandle_t phy_ihandle;
+	pcell_t phy_handle, phy_reg;
+
+	if (OF_getprop(node, "phy-handle", (void *)&phy_handle,
+	    sizeof(phy_handle)) <= 0)
+		return (ENXIO);
+
+	phy_ihandle = (ihandle_t)phy_handle;
+	phy_ihandle = fdt32_to_cpu(phy_ihandle);
+	phy_node = OF_instance_to_package(phy_ihandle);
+
+	if (OF_getprop(phy_node, "reg", (void *)&phy_reg,
+	    sizeof(phy_reg)) <= 0)
+		return (ENXIO);
+
+	*phy_addr = fdt32_to_cpu(phy_reg);
+	return (0);
+}
+
+int
+fdt_get_mem_regions(struct mem_region *mr, int *mrcnt, uint32_t *memsize)
+{
+	pcell_t reg[FDT_REG_CELLS * FDT_MEM_REGIONS];
+	pcell_t *regp;
+	phandle_t memory;
+	uint32_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 <= 0) {
+		rv = ENXIO;
+		goto out;
+	}
+
+	if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
+	    &size_cells)) != 0)
+		goto out;
+
+	if (addr_cells > 2) {
+		rv = ERANGE;
+		goto out;
+	}
+
+	tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
+	reg_len = OF_getproplen(memory, "reg");
+	if (reg_len <= 0 || reg_len > sizeof(reg)) {
+		rv = ERANGE;
+		goto out;
+	}
+
+	if (OF_getprop(memory, "reg", reg, reg_len) <= 0) {
+		rv = ENXIO;
+		goto out;
+	}
+
+	memory_size = 0;
+	tuples = reg_len / tuple_size;
+	regp = (pcell_t *)&reg;
+	for (i = 0; i < tuples; i++) {
+
+		rv = fdt_data_to_res(regp, addr_cells, size_cells,
+			(u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size);
+
+		if (rv != 0)
+			goto out;
+
+		regp += addr_cells + size_cells;
+		memory_size += mr[i].mr_size;
+	}
+
+	if (memory_size == 0) {
+		rv = ERANGE;
+		goto out;
+	}
+
+	*mrcnt = i;
+	*memsize = memory_size;
+	rv = 0;
+out:
+	return (rv);
+}

Added: head/sys/dev/fdt/fdt_common.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/fdt/fdt_common.h	Wed Jun  2 17:17:45 2010	(r208747)
@@ -0,0 +1,112 @@
+/*-
+ * 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 <contrib/libfdt/libfdt_env.h>
+#include <dev/ofw/ofw_bus.h>
+#include <machine/fdt.h>
+
+#define FDT_MEM_REGIONS	8
+
+#define DI_MAX_INTR_NUM	8
+
+struct fdt_pci_range {
+	u_long	base_pci;
+	u_long	base_parent;
+	u_long	len;
+};
+
+struct fdt_pci_intr {
+	int	addr_cells;
+	int	intr_cells;
+	int	map_len;
+	pcell_t	*map;
+	pcell_t	*mask;
+};
+
+struct fdt_sense_level {
+	enum intr_trigger	trig;
+	enum intr_polarity	pol;
+};
+
+typedef int (*fdt_pic_decode_t)(phandle_t, pcell_t *, int *, int *, int *);
+extern fdt_pic_decode_t fdt_pic_table[];
+
+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[];
+
+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
+
+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 *);
+int fdt_data_verify(void *, int);
+phandle_t fdt_find_compatible(phandle_t, const char *, int);
+int fdt_get_mem_regions(struct mem_region *, int *, uint32_t *);
+int fdt_get_phyaddr(phandle_t node, int *);
+int fdt_immr_addr(void);
+int fdt_regsize(phandle_t, u_long *, u_long *);
+int fdt_intr_decode(phandle_t, pcell_t *, int *, int *, int *);
+int fdt_intr_to_rl(phandle_t, struct resource_list *, struct fdt_sense_level *);
+int fdt_is_compatible(phandle_t, const char *);
+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_pci_intr_info(phandle_t, struct fdt_pci_intr *);
+int fdt_pci_ranges(phandle_t, struct fdt_pci_range *, struct fdt_pci_range *);
+int fdt_pci_ranges_decode(phandle_t, struct fdt_pci_range *,
+    struct fdt_pci_range *);
+int fdt_pci_route_intr(int, int, int, int, struct fdt_pci_intr *, int *);
+int fdt_ranges_verify(pcell_t *, int, int, int, int);
+int fdt_reg_to_rl(phandle_t, struct resource_list *, u_long);
+int fdt_pm(phandle_t);
+
+#endif /* _FDT_COMMON_H_ */

Added: head/sys/dev/fdt/fdt_pci.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/fdt/fdt_pci.c	Wed Jun  2 17:17:45 2010	(r208747)
@@ -0,0 +1,365 @@
+/*-
+ * Copyright (c) 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/ktr.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/malloc.h>
+
+#include <dev/fdt/fdt_common.h>
+
+#include <machine/fdt.h>
+
+#include "ofw_bus_if.h"
+
+#define DEBUG
+#undef DEBUG
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
+    printf(fmt,##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+#define FDT_RANGES_CELLS	((3 + 3 + 2) * 2)
+
+static void
+fdt_pci_range_dump(struct fdt_pci_range *range)
+{
+#ifdef DEBUG
+	printf("\n");
+	printf("  base_pci = 0x%08lx\n", range->base_pci);
+	printf("  base_par = 0x%08lx\n", range->base_parent);
+	printf("  len      = 0x%08lx\n", range->len);
+#endif
+}
+
+int
+fdt_pci_ranges_decode(phandle_t node, struct fdt_pci_range *io_space,
+    struct fdt_pci_range *mem_space)
+{
+	pcell_t ranges[FDT_RANGES_CELLS];
+	struct fdt_pci_range *pci_space;
+	pcell_t addr_cells, size_cells, par_addr_cells;
+	pcell_t *rangesptr;
+	pcell_t cell0, cell1, cell2;
+	int tuple_size, tuples, i, rv, offset_cells, len;
+
+	/*
+	 * Retrieve 'ranges' property.
+	 */
+	if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0)
+		return (EINVAL);
+	if (addr_cells != 3 || size_cells != 2)
+		return (ERANGE);
+
+	par_addr_cells = fdt_parent_addr_cells(node);
+	if (par_addr_cells > 3)
+		return (ERANGE);
+
+	len = OF_getproplen(node, "ranges");
+	if (len > sizeof(ranges))
+		return (ENOMEM);
+
+	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;
+
+	rangesptr = &ranges[0];
+	offset_cells = 0;
+	for (i = 0; i < tuples; i++) {
+		cell0 = fdt_data_get((void *)rangesptr, 1);
+		rangesptr++;
+		cell1 = fdt_data_get((void *)rangesptr, 1);
+		rangesptr++;
+		cell2 = fdt_data_get((void *)rangesptr, 1);
+		rangesptr++;
+
+		if (cell0 & 0x02000000) {
+			pci_space = mem_space;
+		} else if (cell0 & 0x01000000) {
+			pci_space = io_space;
+		} else {
+			rv = ERANGE;
+			goto out;
+		}
+
+		if (par_addr_cells == 3) {
+			/*
+			 * This is a PCI subnode 'ranges'. Skip cell0 and
+			 * cell1 of this entry and only use cell2.
+			 */
+			offset_cells = 2;
+			rangesptr += offset_cells;
+		}
+
+		if (fdt_data_verify((void *)rangesptr, par_addr_cells -
+		    offset_cells)) {
+			rv = ERANGE;
+			goto out;
+		}
+		pci_space->base_parent = fdt_data_get((void *)rangesptr,
+		    par_addr_cells - offset_cells);
+		rangesptr += par_addr_cells - offset_cells;
+
+		if (fdt_data_verify((void *)rangesptr, size_cells)) {
+			rv = ERANGE;
+			goto out;
+		}
+		pci_space->len = fdt_data_get((void *)rangesptr, size_cells);
+		rangesptr += size_cells;
+
+		pci_space->base_pci = cell2;
+	}
+	rv = 0;
+out:
+	return (rv);
+}
+
+int
+fdt_pci_ranges(phandle_t node, struct fdt_pci_range *io_space,
+    struct fdt_pci_range *mem_space)
+{
+	struct fdt_pci_range par_io_space, par_mem_space;
+	u_long base;
+	int err;
+
+	debugf("Processing parent PCI node: %x\n", node);
+	if ((err = fdt_pci_ranges_decode(OF_parent(node), &par_io_space,
+	    &par_mem_space)) != 0) {
+		debugf("could not decode parent PCI node 'ranges'\n");
+		return (err);
+	}
+
+	debugf("Processing PCI sub node: %x\n", node);
+	if ((err = fdt_pci_ranges_decode(node, io_space, mem_space)) != 0) {
+		debugf("could not decode PCI subnode 'ranges'\n");
+		return (err);
+	}
+
+	base = io_space->base_parent & 0x000fffff;
+	base += par_io_space.base_parent;
+	io_space->base_parent = base;
+
+	base = mem_space->base_parent & 0x000fffff;
+	base += par_mem_space.base_parent;
+	mem_space->base_parent = base;
+
+	debugf("Post fixup dump:\n");
+	fdt_pci_range_dump(io_space);
+	fdt_pci_range_dump(mem_space);
+
+	return (0);
+}
+
+static int
+fdt_addr_cells(phandle_t node, int *addr_cells)
+{
+	pcell_t cell;
+	int cell_size;
+
+	cell_size = sizeof(cell);
+	if (OF_getprop(node, "#address-cells", &cell, cell_size) < cell_size)
+		return (EINVAL);
+	*addr_cells = fdt32_to_cpu((int)cell);
+
+	if (*addr_cells > 3)
+		return (ERANGE);
+	return (0);
+}
+
+static int
+fdt_interrupt_cells(phandle_t node)
+{
+	pcell_t intr_cells;
+
+	if (OF_getprop(node, "#interrupt-cells", &intr_cells,
+	    sizeof(intr_cells)) <= 0) {
+		debugf("no intr-cells defined, defaulting to 1\n");
+		intr_cells = 1;
+	}
+	intr_cells = fdt32_to_cpu(intr_cells);
+
+	return ((int)intr_cells);
+}
+
+int
+fdt_pci_intr_info(phandle_t node, struct fdt_pci_intr *intr_info)
+{
+	void *map, *mask;
+	phandle_t pci_par;
+	int intr_cells, addr_cells;
+	int len;
+
+	addr_cells = fdt_parent_addr_cells(node);
+
+	pci_par = OF_parent(node);
+	intr_cells = fdt_interrupt_cells(pci_par);
+
+	/*
+	 * Retrieve the interrupt map and mask properties.
+	 */
+	len = OF_getprop_alloc(pci_par, "interrupt-map-mask", 1, &mask);
+	if (len / sizeof(pcell_t) != (addr_cells + intr_cells)) {
+		debugf("bad mask len = %d\n", len);
+		goto err;
+	}
+
+	len = OF_getprop_alloc(pci_par, "interrupt-map", 1, &map);
+	if (len <= 0) {
+		debugf("bad map len = %d\n", len);
+		goto err;
+	}
+
+	intr_info->map_len = len;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201006021717.o52HHjfW017538>