From patchwork Thu May 8 22:23:41 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rob Herring X-Patchwork-Id: 4139251 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 1A3C4BFF02 for ; Thu, 8 May 2014 22:33:58 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BCA80202DD for ; Thu, 8 May 2014 22:33:57 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9F953201E7 for ; Thu, 8 May 2014 22:33:56 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WiWr3-0002if-JZ; Thu, 08 May 2014 22:31:53 +0000 Received: from mail-ob0-x229.google.com ([2607:f8b0:4003:c01::229]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1WiWr0-0002dD-Bx for linux-arm-kernel@lists.infradead.org; Thu, 08 May 2014 22:31:51 +0000 Received: by mail-ob0-f169.google.com with SMTP id vb8so3914189obc.0 for ; Thu, 08 May 2014 15:31:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=tXwhKAjUy7iuk9YHmNAPWO3PNcXiBdmBKzr43PxcRwY=; b=f+0IT7Ag5xSfFWDCDauRaH/86Hqr9WEyRzsFXtlD1ONi7EH2eppuqz+VkIXfTURX5d DHSXfO2SKksp1Krt7C4BRY9G6u3NEw/55NDkfrADVF4BYgdyJoiJcvkRkgEw+BYPUQMr lP+CNLVxa+i/aiUvdKT6UsIGkHRYAGB4TPO66zLsCWb1xfVkLQJjpvV384vBCzwgl+4B sYX+sJFomrO2HywfZO1SMM/ryBQgiMlQEIQpxscX+hwSYrAZNF4vaomOcYmjEjah9NtO gVY0ctJP5AIKoczcphRp5FjmlDhOBZCWCaruA8xEFyNCCnujRN6BzrOUQI+JC6AKa81e HQ1w== X-Received: by 10.60.125.195 with SMTP id ms3mr8749484oeb.3.1399587841330; Thu, 08 May 2014 15:24:01 -0700 (PDT) Received: from localhost.localdomain (72-48-77-163.dyn.grandenetworks.net. [72.48.77.163]) by mx.google.com with ESMTPSA id fj9sm9311932oeb.6.2014.05.08.15.23.59 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 08 May 2014 15:24:00 -0700 (PDT) From: Rob Herring To: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [PATCH 4/6] of/fdt: add FDT address translation support Date: Thu, 8 May 2014 17:23:41 -0500 Message-Id: <1399587823-17701-5-git-send-email-robherring2@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1399587823-17701-1-git-send-email-robherring2@gmail.com> References: <1399587823-17701-1-git-send-email-robherring2@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140508_153150_518702_246907A8 X-CRM114-Status: GOOD ( 24.96 ) X-Spam-Score: 0.1 (/) Cc: Grant Likely , benh@kernel.crashing.org, Rob Herring , Arnd Bergmann , Greg Kroah-Hartman X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.4 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RP_MATCHES_RCVD, T_DKIM_INVALID, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Rob Herring Copy u-boot's FDT address translation code from common/fdt_support. This code was originally based on the kernel's unflattened DT address parsing code. This commit can be reverted once relicensing of this code to GPLv2/BSD is done and it is added to libfdt. Signed-off-by: Rob Herring Cc: Grant Likely --- drivers/of/Makefile | 2 + drivers/of/fdt_address.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_fdt.h | 1 + 3 files changed, 244 insertions(+) create mode 100644 drivers/of/fdt_address.c diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 9891232..099b1fb 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,5 +1,6 @@ obj-y = base.o device.o platform.o obj-$(CONFIG_OF_FLATTREE) += fdt.o +obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_IRQ) += irq.o @@ -12,3 +13,4 @@ obj-$(CONFIG_OF_MTD) += of_mtd.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o CFLAGS_fdt.o = -I$(src)/../../scripts/dtc/libfdt +CFLAGS_fdt_address.o = -I$(src)/../../scripts/dtc/libfdt diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c new file mode 100644 index 0000000..8d3dc6f --- /dev/null +++ b/drivers/of/fdt_address.c @@ -0,0 +1,241 @@ +/* + * FDT Address translation based on u-boot fdt_support.c which in turn was + * based on the kernel unflattened DT address translation code. + * + * (C) Copyright 2007 + * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com + * + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + */ +#include +#include +#include +#include +#include + +/* Max address size we deal with */ +#define OF_MAX_ADDR_CELLS 4 +#define OF_CHECK_COUNTS(na, ns) ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && \ + (ns) > 0) + +/* Debug utility */ +#ifdef DEBUG +static void __init of_dump_addr(const char *s, const __be32 *addr, int na) +{ + pr_debug("%s", s); + while(na--) + pr_cont(" %08x", *(addr++)); + pr_debug("\n"); +} +#else +static void __init of_dump_addr(const char *s, const __be32 *addr, int na) { } +#endif + +/* Callbacks for bus specific translators */ +struct of_bus { + void (*count_cells)(const void *blob, int parentoffset, + int *addrc, int *sizec); + u64 (*map)(__be32 *addr, const __be32 *range, + int na, int ns, int pna); + int (*translate)(__be32 *addr, u64 offset, int na); +}; + +/* Default translator (generic bus) */ +static void __init fdt_bus_default_count_cells(const void *blob, int parentoffset, + int *addrc, int *sizec) +{ + const __be32 *prop; + + if (addrc) { + prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL); + if (prop) + *addrc = be32_to_cpup(prop); + else + *addrc = dt_root_addr_cells; + } + + if (sizec) { + prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); + if (prop) + *sizec = be32_to_cpup(prop); + else + *sizec = dt_root_size_cells; + } +} + +static u64 __init fdt_bus_default_map(__be32 *addr, const __be32 *range, + int na, int ns, int pna) +{ + u64 cp, s, da; + + cp = of_read_number(range, na); + s = of_read_number(range + na + pna, ns); + da = of_read_number(addr, na); + + pr_debug("FDT: default map, cp=%llx, s=%llx, da=%llx\n", + cp, s, da); + + if (da < cp || da >= (cp + s)) + return OF_BAD_ADDR; + return da - cp; +} + +static int __init fdt_bus_default_translate(__be32 *addr, u64 offset, int na) +{ + u64 a = of_read_number(addr, na); + memset(addr, 0, na * 4); + a += offset; + if (na > 1) + addr[na - 2] = cpu_to_fdt32(a >> 32); + addr[na - 1] = cpu_to_fdt32(a & 0xffffffffu); + + return 0; +} + +/* Array of bus specific translators */ +static const struct of_bus of_busses[] __initconst = { + /* Default */ + { + .count_cells = fdt_bus_default_count_cells, + .map = fdt_bus_default_map, + .translate = fdt_bus_default_translate, + }, +}; + +static int __init fdt_translate_one(const void *blob, int parent, + const struct of_bus *bus, + const struct of_bus *pbus, __be32 *addr, + int na, int ns, int pna, const char *rprop) +{ + const __be32 *ranges; + int rlen; + int rone; + u64 offset = OF_BAD_ADDR; + + ranges = fdt_getprop(blob, parent, rprop, &rlen); + if (!ranges) + return 1; + if (rlen == 0) { + offset = of_read_number(addr, na); + memset(addr, 0, pna * 4); + pr_debug("FDT: empty ranges, 1:1 translation\n"); + goto finish; + } + + pr_debug("FDT: walking ranges...\n"); + + /* Now walk through the ranges */ + rlen /= 4; + rone = na + pna + ns; + for (; rlen >= rone; rlen -= rone, ranges += rone) { + offset = bus->map(addr, ranges, na, ns, pna); + if (offset != OF_BAD_ADDR) + break; + } + if (offset == OF_BAD_ADDR) { + pr_debug("FDT: not found !\n"); + return 1; + } + memcpy(addr, ranges + na, 4 * pna); + + finish: + of_dump_addr("FDT: parent translation for:", addr, pna); + pr_debug("FDT: with offset: %llx\n", offset); + + /* Translate it into parent bus space */ + return pbus->translate(addr, offset, pna); +} + +/* + * Translate an address from the device-tree into a CPU physical address, + * this walks up the tree and applies the various bus mappings on the + * way. + * + * Note: We consider that crossing any level with #size-cells == 0 to mean + * that translation is impossible (that is we are not dealing with a value + * that can be mapped to a cpu physical address). This is not really specified + * that way, but this is traditionally the way IBM at least do things + */ +u64 __init fdt_translate_address(const void *blob, int node_offset) +{ + int parent, len; + const struct of_bus *bus, *pbus; + const __be32 *reg; + __be32 addr[OF_MAX_ADDR_CELLS]; + int na, ns, pna, pns; + u64 result = OF_BAD_ADDR; + + pr_debug("FDT: ** translation for device %s **\n", + fdt_get_name(blob, node_offset, NULL)); + + reg = fdt_getprop(blob, node_offset, "reg", &len); + if (!reg) { + pr_err("FDT: warning: device tree node '%s' has no address.\n", + fdt_get_name(blob, node_offset, NULL)); + goto bail; + } + + /* Get parent & match bus type */ + parent = fdt_parent_offset(blob, node_offset); + if (parent < 0) + goto bail; + bus = &of_busses[0]; + + /* Cound address cells & copy address locally */ + bus->count_cells(blob, parent, &na, &ns); + if (!OF_CHECK_COUNTS(na, ns)) { + pr_err("FDT: Bad cell count for %s\n", + fdt_get_name(blob, node_offset, NULL)); + goto bail; + } + memcpy(addr, reg, na * 4); + + pr_debug("FDT: bus (na=%d, ns=%d) on %s\n", + na, ns, fdt_get_name(blob, parent, NULL)); + of_dump_addr("OF: translating address:", addr, na); + + /* Translate */ + for (;;) { + /* Switch to parent bus */ + node_offset = parent; + parent = fdt_parent_offset(blob, node_offset); + + /* If root, we have finished */ + if (parent < 0) { + pr_debug("FDT: reached root node\n"); + result = of_read_number(addr, na); + break; + } + + /* Get new parent bus and counts */ + pbus = &of_busses[0]; + pbus->count_cells(blob, parent, &pna, &pns); + if (!OF_CHECK_COUNTS(pna, pns)) { + pr_err("FDT: Bad cell count for %s\n", + fdt_get_name(blob, node_offset, NULL)); + break; + } + + pr_debug("FDT: parent bus (na=%d, ns=%d) on %s\n", + pna, pns, fdt_get_name(blob, parent, NULL)); + + /* Apply bus translation */ + if (fdt_translate_one(blob, node_offset, bus, pbus, + addr, na, ns, pna, "ranges")) + break; + + /* Complete the move up one level */ + na = pna; + ns = pns; + bus = pbus; + + of_dump_addr("FDT: one level translation:", addr, na); + } + bail: + return result; +} diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 5c0ab05..0511789 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h @@ -83,6 +83,7 @@ extern void unflatten_device_tree(void); extern void unflatten_and_copy_device_tree(void); extern void early_init_devtree(void *); extern void early_get_first_memblock_info(void *, phys_addr_t *); +extern u64 fdt_translate_address(const void *blob, int node_offset); #else /* CONFIG_OF_FLATTREE */ static inline void early_init_fdt_scan_reserved_mem(void) {} static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }