From owner-svn-src-head@FreeBSD.ORG Thu Nov 20 17:03:41 2014 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 5A9BC425; Thu, 20 Nov 2014 17:03:41 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 47858834; Thu, 20 Nov 2014 17:03:41 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id sAKH3fLe032206; Thu, 20 Nov 2014 17:03:41 GMT (envelope-from andrew@FreeBSD.org) Received: (from andrew@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id sAKH3fmH032205; Thu, 20 Nov 2014 17:03:41 GMT (envelope-from andrew@FreeBSD.org) Message-Id: <201411201703.sAKH3fmH032205@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: andrew set sender to andrew@FreeBSD.org using -f From: Andrew Turner Date: Thu, 20 Nov 2014 17:03:41 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r274751 - head/sys/dev/fdt X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 20 Nov 2014 17:03:41 -0000 Author: andrew Date: Thu Nov 20 17:03:40 2014 New Revision: 274751 URL: https://svnweb.freebsd.org/changeset/base/274751 Log: The ranges parent bus address may just be a tag to the entry in the parent node. Take this in to account by searching until we find the range for the root node. Differential Revision: https://reviews.freebsd.org/D1160 Reviewed by: ian Obtained from: ABT Systems Ltd Sponsored by: The FreeBSD Foundation Modified: head/sys/dev/fdt/fdt_common.c Modified: head/sys/dev/fdt/fdt_common.c ============================================================================== --- head/sys/dev/fdt/fdt_common.c Thu Nov 20 16:31:31 2014 (r274750) +++ head/sys/dev/fdt/fdt_common.c Thu Nov 20 17:03:40 2014 (r274751) @@ -1,7 +1,9 @@ /*- - * Copyright (c) 2009-2010 The FreeBSD Foundation + * 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. * @@ -64,12 +66,84 @@ vm_offset_t fdt_immr_size; struct fdt_ic_list fdt_ic_list_head = SLIST_HEAD_INITIALIZER(fdt_ic_list_head); +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 ((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) { + *base = 0; + *size = ULONG_MAX; + return (0); + } + + 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 (fdt_ranges_verify(ranges, tuples, par_addr_cells, + addr_cells, size_cells)) { + 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; - int len, tuple_size, tuples; + 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); @@ -109,8 +183,17 @@ fdt_get_range(phandle_t node, int range_ *base = fdt_data_get((void *)rangesptr, addr_cells); rangesptr += addr_cells; - *base += fdt_data_get((void *)rangesptr, par_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); } @@ -292,7 +375,7 @@ fdt_parent_addr_cells(phandle_t node) /* 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 (2); return ((int)fdt32_to_cpu(addr_cells)); }