From patchwork Thu Feb 20 10:52:41 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 25001 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ie0-f199.google.com (mail-ie0-f199.google.com [209.85.223.199]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 4927F2066E for ; Thu, 20 Feb 2014 10:53:19 +0000 (UTC) Received: by mail-ie0-f199.google.com with SMTP id rl12sf6618824iec.2 for ; Thu, 20 Feb 2014 02:53:18 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:from:to:date:message-id:in-reply-to :references:cc:subject:precedence:list-id:list-unsubscribe :list-archive:list-post:list-help:list-subscribe:mime-version :errors-to:sender:x-original-sender :x-original-authentication-results:mailing-list:content-type :content-transfer-encoding; bh=l+1+4caThzTTIa5XXx6xzCES+LeBpNbAkD0cZZQPH+g=; b=a5pkwHAS3qaKKoLh9MehEZaJ4+dCux86HWpqrPUNT84SOyx3SfdtqFiZjuirVKej0O VMYAE0DDzNt4VsJ/yEKY1pF58J4QaCfTrwUCOfUhYPpcfohdoBSJlpHM39wzVFNQbVx/ hdMRWnFpdqVHCamt+3pT7LeBNT9LUHhLCP98t8QBR7K0bR4cP96lJzjZ36SbQk1oNs9j rQI84MDAYH9w2yEsNxnIVPTttOLlrmJxOaLLEbjc1uLIEH1YqpiyUWENxD1ODeE7EnF7 QKjVk1Y5/8iqscE0vbaSCEBRU652x40mfXB1bMUpyXkApc89tnqdz3yGVGJsh0Ht1f1E ltTw== X-Gm-Message-State: ALoCoQmQZ/LoXpAegK2jNKxGCtnQNaYNIcAQFVAzl4lxLxCYM2vM6wZ7bg99FNSPv0UUgd9hVMcB X-Received: by 10.50.47.6 with SMTP id z6mr810401igm.4.1392893598280; Thu, 20 Feb 2014 02:53:18 -0800 (PST) X-BeenThere: patchwork-forward@linaro.org Received: by 10.140.94.41 with SMTP id f38ls457401qge.55.gmail; Thu, 20 Feb 2014 02:53:18 -0800 (PST) X-Received: by 10.221.66.73 with SMTP id xp9mr596797vcb.27.1392893598176; Thu, 20 Feb 2014 02:53:18 -0800 (PST) Received: from mail-vc0-f175.google.com (mail-vc0-f175.google.com [209.85.220.175]) by mx.google.com with ESMTPS id e9si1305027vct.109.2014.02.20.02.53.18 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Thu, 20 Feb 2014 02:53:18 -0800 (PST) Received-SPF: neutral (google.com: 209.85.220.175 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.220.175; Received: by mail-vc0-f175.google.com with SMTP id ij19so1655095vcb.6 for ; Thu, 20 Feb 2014 02:53:18 -0800 (PST) X-Received: by 10.220.81.71 with SMTP id w7mr557672vck.71.1392893598075; Thu, 20 Feb 2014 02:53:18 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.220.174.196 with SMTP id u4csp48352vcz; Thu, 20 Feb 2014 02:53:17 -0800 (PST) X-Received: by 10.194.175.202 with SMTP id cc10mr1346172wjc.48.1392893596536; Thu, 20 Feb 2014 02:53:16 -0800 (PST) Received: from ip-10-141-164-156.ec2.internal (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTPS id x1si4693064wie.77.2014.02.20.02.53.15 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Thu, 20 Feb 2014 02:53:16 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linaro-mm-sig-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) client-ip=54.225.227.206; Received: from localhost ([127.0.0.1] helo=ip-10-141-164-156.ec2.internal) by ip-10-141-164-156.ec2.internal with esmtp (Exim 4.76) (envelope-from ) id 1WGRET-0007U9-Pz; Thu, 20 Feb 2014 10:51:57 +0000 Received: from mailout2.w1.samsung.com ([210.118.77.12]) by ip-10-141-164-156.ec2.internal with esmtp (Exim 4.76) (envelope-from ) id 1WGREH-0007TO-Lx for linaro-mm-sig@lists.linaro.org; Thu, 20 Feb 2014 10:51:45 +0000 Received: from eucpsbgm2.samsung.com (unknown [203.254.199.245]) by mailout2.w1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0N1A0058WJK24200@mailout2.w1.samsung.com> for linaro-mm-sig@lists.linaro.org; Thu, 20 Feb 2014 10:52:51 +0000 (GMT) X-AuditID: cbfec7f5-b7fc96d000004885-15-5305de87028e Received: from eusync2.samsung.com ( [203.254.199.212]) by eucpsbgm2.samsung.com (EUCPMTA) with SMTP id D4.C1.18565.78ED5035; Thu, 20 Feb 2014 10:52:55 +0000 (GMT) Received: from amdc1339.mshome.net ([106.116.147.30]) by eusync2.samsung.com (Oracle Communications Messaging Server 7u4-23.01 (7.0.4.23.0) 64bit (built Aug 10 2011)) with ESMTPA id <0N1A005WSJK1HXB0@eusync2.samsung.com>; Thu, 20 Feb 2014 10:52:55 +0000 (GMT) From: Marek Szyprowski To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linaro-mm-sig@lists.linaro.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Date: Thu, 20 Feb 2014 11:52:41 +0100 Message-id: <1392893567-31623-2-git-send-email-m.szyprowski@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-reply-to: <1392893567-31623-1-git-send-email-m.szyprowski@samsung.com> References: <1392893567-31623-1-git-send-email-m.szyprowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAAy2RW0ySYRiA+37+EyTtD6n+nHNFdZEVafPiW1HW1tbXRc3Nm1YXSkrqAnGg LsktR2iKikRtHlLT1KYMtch5YNaU8DCbJkFYS2mNLlJJM9S2Qku0u+d99rzvzUtzBA/xCDoj M1umypTKRSQPf7M+4j52z0skxnR7BHDNNEzBH9pCDlx8WgbgY8cEASv8jQQcCPYC6C8vxmFf xwKA41o/BXtKqii47P6CQavPQ8DGomYcumy1JGx3zFDw2ewgBlumnBhsGNERMFgZBcemAiS0 OJooaKxsx2HhSwcF13qsOJwbLMJhZ9U8Ds22IICzS6P42UhkqbcA9Oe3CaD5B0aAXIZyDPXV zFCozK8jkNVcQqJpTz+JXjTfQZ/WfRzkHtUSqCM4h6GKtRhUZzMAZOgyAxSwRiXsvMqTpMrk Gbky1fEzybz09wU6Mmu1Adwa8nSBAqDTAj3g0iwTx653TWBbvJud9HaSesCjBUwLYL0/Lf+H Uowd973e3CCZWFb/XU+GWMg0AjZQj4ciDvOZZIudZZunwpkE1uo3b0Q0jTOH2PutmpDmM4hd rn6ChTTL7GNrTZKQ5jIX2bveGSLEgo3EVuMijIDfALaZwS5ZTkqW+nqa4oRYLVWoczLTxClK hRVsfXKlF7QMn7QDhgaiMP6VKTxRQEhz1XkKO2BpjkjI7/5IJAr4qdI8jUylTFLlyGVqO8Bo bkQBoOLHV0+38k8l6bTZkfX2b3H856Tb9AjzpkYfSOYerG2Lv2Dezij3Gy9JhW2Gv03Ve51y 51gN+W5lj/7tZeWRr/ahD8K524GbYk330cH8hU5M4qsbnT9sbXLR52/0rmqqrg2cK85/pewP 20H90jYHpssnJgsXS8PFkT3RCsmSCFenS2OjOSq19B87/xfFpwIAAA== Cc: Mark Rutland , Benjamin Herrenschmidt , Tomasz Figa , Will Deacon , Tomasz Figa , Paul Mackerras , Arnd Bergmann , Josh Cartwright , Catalin Marinas , Grant Likely , Ian Campbell , Pawel Moll , Stephen Warren , Sascha Hauer , Michal Nazarewicz , Rob Herring , Kumar Gala , Olof Johansson Subject: [Linaro-mm-sig] [PATCH v4 1/7] drivers: of: add initialization code for reserved memory X-BeenThere: linaro-mm-sig@lists.linaro.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: , List-Help: , List-Subscribe: , MIME-Version: 1.0 Errors-To: linaro-mm-sig-bounces@lists.linaro.org Sender: linaro-mm-sig-bounces@lists.linaro.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: m.szyprowski@samsung.com X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.175 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 This patch adds device tree support for contiguous and reserved memory regions defined in device tree. Large memory blocks can be reliably reserved only during early boot. This must happen before the whole memory management subsystem is initialized, because we need to ensure that the given contiguous blocks are not yet allocated by kernel. Also it must happen before kernel mappings for the whole low memory are created, to ensure that there will be no mappings (for reserved blocks) or mapping with special properties can be created (for CMA blocks). This all happens before device tree structures are unflattened, so we need to get reserved memory layout directly from fdt. Later, those reserved memory regions are assigned to devices on each device structure initialization. Signed-off-by: Marek Szyprowski [joshc: rework to implement new DT binding, provide mechanism for plugging in new reserved-memory node handlers via RESERVEDMEM_OF_DECLARE] Signed-off-by: Josh Cartwright [mszyprow: added generic memory reservation code, refactored code to put it directly into fdt.c] Signed-off-by: Marek Szyprowski --- drivers/of/Kconfig | 6 + drivers/of/Makefile | 1 + drivers/of/fdt.c | 145 ++++++++++++++++++ drivers/of/of_reserved_mem.c | 296 +++++++++++++++++++++++++++++++++++++ drivers/of/platform.c | 7 + include/asm-generic/vmlinux.lds.h | 11 ++ include/linux/of_reserved_mem.h | 65 ++++++++ 7 files changed, 531 insertions(+) create mode 100644 drivers/of/of_reserved_mem.c create mode 100644 include/linux/of_reserved_mem.h diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index c6973f101a3e..30a7d87a8077 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -75,4 +75,10 @@ config OF_MTD depends on MTD def_bool y +config OF_RESERVED_MEM + depends on OF_EARLY_FLATTREE + bool + help + Helpers to allow for reservation of memory regions + endmenu # OF diff --git a/drivers/of/Makefile b/drivers/of/Makefile index efd05102c405..ed9660adad77 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o obj-$(CONFIG_OF_MTD) += of_mtd.o +obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 758b4f8b30b7..04efe2ba736f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -438,6 +439,150 @@ int __initdata dt_root_size_cells; struct boot_param_header *initial_boot_params; #ifdef CONFIG_OF_EARLY_FLATTREE +#if defined(CONFIG_HAVE_MEMBLOCK) +int __init __weak +early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, + bool nomap) +{ + if (memblock_is_region_reserved(base, size)) + return -EBUSY; + if (nomap) + return memblock_remove(base, size); + return memblock_reserve(base, size); +} +#else +int __init __weak +early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, + bool nomap) +{ + pr_error("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n", + base, size, nomap ? " (nomap)" : ""); + return -ENOSYS; +} +#endif + +/** + * res_mem_reserve_reg() - reserve all memory described in 'reg' property + */ +static int __init +__reserved_mem_reserve_reg(unsigned long node, const char *uname, + phys_addr_t *res_base, phys_addr_t *res_size) +{ + int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); + phys_addr_t base, size; + unsigned long len; + __be32 *prop; + int nomap; + + prop = of_get_flat_dt_prop(node, "reg", &len); + if (!prop) + return -ENOENT; + + if (len && len % t_len != 0) { + pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", + uname); + return -EINVAL; + } + + nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; + + /* store base and size values from the first reg tuple */ + *res_base = 0; + while (len > 0) { + base = dt_mem_next_cell(dt_root_addr_cells, &prop); + size = dt_mem_next_cell(dt_root_size_cells, &prop); + + if (base && size && + early_init_dt_reserve_memory_arch(base, size, nomap) == 0) + pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n", + uname, &base, (unsigned long)size / SZ_1M); + else + pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n", + uname, &base, (unsigned long)size / SZ_1M); + + len -= t_len; + + if (!(*res_base)) { + *res_base = base; + *res_size = size; + } + } + return 0; +} + +static int __reserved_mem_check_root(unsigned long node) +{ + __be32 *prop; + + prop = of_get_flat_dt_prop(node, "#size-cells", NULL); + if (prop && be32_to_cpup(prop) != dt_root_size_cells) + return -EINVAL; + + prop = of_get_flat_dt_prop(node, "#address-cells", NULL); + if (prop && be32_to_cpup(prop) != dt_root_addr_cells) + return -EINVAL; + + prop = of_get_flat_dt_prop(node, "ranges", NULL); + if (!prop) + return -EINVAL; + return 0; +} + +/** + * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory + */ +static int __init +__fdt_scan_reserved_mem(unsigned long node, const char *uname, int depth, + void *data) +{ + phys_addr_t base = 0, size = 0; + static int found; + const char *status; + int err; + + if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { + if (__reserved_mem_check_root(node) != 0) { + pr_err("Reserved memory: unsupported node format, ignoring\n"); + /* break scan */ + return 1; + } + found = 1; + /* scan next node */ + return 0; + } else if (!found) { + /* scan next node */ + return 0; + } else if (found && depth < 2) { + /* scanning of /reserved-memory has been finished */ + return 1; + } + + status = of_get_flat_dt_prop(node, "status", NULL); + if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) + return 0; + + err = __reserved_mem_reserve_reg(node, uname, &base, &size); + if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL) == NULL) + goto end; + + fdt_reserved_mem_save_node(node, uname, base, size); +end: + /* scan next node */ + return 0; +} + +/** + * early_init_fdt_scan_reserved_mem() - create reserved memory regions + * + * This function grabs memory from early allocator for device exclusive use + * defined in device tree structures. It should be called by arch specific code + * once the early allocator (i.e. memblock) has been fully activated. + */ +void __init early_init_fdt_scan_reserved_mem(void) +{ + of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); + fdt_init_reserved_mem(); +} /** * of_scan_flat_dt - scan flattened tree blob and call callback on each. diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c new file mode 100644 index 000000000000..cacf04810b87 --- /dev/null +++ b/drivers/of/of_reserved_mem.c @@ -0,0 +1,296 @@ +/* + * Device tree based initialization code for reserved memory. + * + * Copyright (c) 2013, The Linux Foundation. All Rights Reserved. + * Copyright (c) 2013,2014 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Author: Marek Szyprowski + * Author: Josh Cartwright + * + * 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 of the + * License or (at your optional) any later version of the license. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_RESERVED_REGIONS 16 +static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS]; +static int reserved_mem_count; + +#if defined(CONFIG_HAVE_MEMBLOCK) +#include +int __init __weak +early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, phys_addr_t align, + phys_addr_t start, phys_addr_t end, + bool nomap, phys_addr_t *res_base) +{ + /* + * We use __memblock_alloc_base() since memblock_alloc_base() panic()s. + */ + phys_addr_t base = __memblock_alloc_base(size, align, end); + if (!base) + return -ENOMEM; + + if (base < start) { + memblock_free(base, size); + return -ENOMEM; + } + + *res_base = base; + if (nomap) + return memblock_remove(base, size); + return 0; +} +#else +int __init __weak +early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, phys_addr_t align, + phys_addr_t start, phys_addr_t end, + bool nomap, phys_addr_t *res_base) +{ + pr_error("Reserved memory not supported, ignoring region 0x%llx%s\n", + size, nomap ? " (nomap)" : ""); + return -ENOSYS; +} +#endif + +/** + * res_mem_save_node() - save fdt node for second pass initialization + */ +int __init fdt_reserved_mem_save_node(unsigned long node, const char *uname, + phys_addr_t base, phys_addr_t size) +{ + struct reserved_mem *rmem = &reserved_mem[reserved_mem_count]; + + if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) { + pr_err("Reserved memory: not enough space all defined regions.\n"); + return -ENOSPC; + } + + rmem->fdt_node = node; + rmem->name = uname; + rmem->base = base; + rmem->size = size; + + reserved_mem_count++; + return 0; +} + +/** + * res_mem_alloc_size() - allocate reserved memory described by 'size', 'align' + * and 'alloc-ranges' properties + */ +static int __init +__reserved_mem_alloc_size(unsigned long node, const char *uname, + phys_addr_t *res_base, phys_addr_t *res_size) +{ + int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); + phys_addr_t start = 0, end = 0; + phys_addr_t base = 0, align = 0, size; + unsigned long len; + __be32 *prop; + int nomap; + int ret; + + prop = of_get_flat_dt_prop(node, "size", &len); + if (!prop) + return -EINVAL; + + if (len != dt_root_size_cells * sizeof(__be32)) { + pr_err("Reserved memory: invalid size property in '%s' node.\n", + uname); + return -EINVAL; + } + size = dt_mem_next_cell(dt_root_size_cells, &prop); + + nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; + + prop = of_get_flat_dt_prop(node, "align", &len); + if (prop) { + if (len != dt_root_addr_cells * sizeof(__be32)) { + pr_err("Reserved memory: invalid align property in '%s' node.\n", + uname); + return -EINVAL; + } + align = dt_mem_next_cell(dt_root_addr_cells, &prop); + } + + prop = of_get_flat_dt_prop(node, "alloc-ranges", &len); + if (prop) { + + if (len % t_len != 0) { + pr_err("Reserved memory: invalid alloc-ranges property in '%s', skipping node.\n", + uname); + return -EINVAL; + } + + base = 0; + + while (len > 0) { + start = dt_mem_next_cell(dt_root_addr_cells, &prop); + end = start + dt_mem_next_cell(dt_root_size_cells, + &prop); + + ret = early_init_dt_alloc_reserved_memory_arch(size, + align, start, end, nomap, &base); + if (ret == 0) { + pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", + uname, &base, + (unsigned long)size / SZ_1M); + break; + } + len -= t_len; + } + + } else { + ret = early_init_dt_alloc_reserved_memory_arch(size, align, + 0, 0, nomap, &base); + if (ret == 0) + pr_debug("Reserved memory: allocated memory for '%s' node: base %pa, size %ld MiB\n", + uname, &base, (unsigned long)size / SZ_1M); + } + + if (base == 0) { + pr_info("Reserved memory: failed to allocate memory for node '%s'\n", + uname); + return -ENOMEM; + } + + *res_base = base; + *res_size = size; + + return 0; +} + +static const struct of_device_id __rmem_of_table_sentinel + __used __section(__reservedmem_of_table_end); + +/** + * res_mem_init_node() - call region specific reserved memory init code + */ +static int __init __reserved_mem_init_node(struct reserved_mem *rmem) +{ + extern const struct of_device_id __reservedmem_of_table[]; + const struct of_device_id *i; + + for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) { + reservedmem_of_init_fn initfn = i->data; + const char *compat = i->compatible; + + if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) + continue; + + if (initfn(rmem, rmem->fdt_node, rmem->name) == 0) { + pr_info("Reserved memory: initialized node %s, compatible id %s\n", + rmem->name, compat); + return 0; + } + } + return -ENOENT; +} + +/** + * fdt_init_reserved_mem - allocate and init all saved reserved memory regions + */ +void __init fdt_init_reserved_mem(void) +{ + int i; + for (i = 0; i < reserved_mem_count; i++) { + struct reserved_mem *rmem = &reserved_mem[i]; + unsigned long node = rmem->fdt_node; + unsigned long len; + __be32 *prop; + int err = 0; + + prop = of_get_flat_dt_prop(node, "phandle", &len); + if (!prop) + prop = of_get_flat_dt_prop(node, "linux,phandle", &len); + if (prop) + rmem->phandle = of_read_number(prop, len/4); + + if (rmem->size == 0) + err = __reserved_mem_alloc_size(node, rmem->name, + &rmem->base, &rmem->size); + if (err == 0) + __reserved_mem_init_node(rmem); + } +} + +static inline struct reserved_mem *__find_rmem(struct device_node *node) +{ + unsigned int len, phandle_val; + const __be32 *prop; + unsigned int i; + + prop = of_get_property(node, "phandle", &len); + if (!prop) + prop = of_get_property(node, "linux,phandle", &len); + if (!prop || len < sizeof(__be32)) + return NULL; + + phandle_val = be32_to_cpup(prop); + for (i = 0; i < reserved_mem_count; i++) + if (reserved_mem[i].phandle == phandle_val) + return &reserved_mem[i]; + return NULL; +} + +/** + * of_reserved_mem_device_init() - assign reserved memory region to given device + * + * This function assign memory region pointed by "memory-region" device tree + * property to the given device. + */ +void of_reserved_mem_device_init(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct reserved_mem *rmem; + struct of_phandle_args s; + unsigned int i; + + for (i = 0; of_parse_phandle_with_args(np, "memory-region", + "#memory-region-cells", i, &s) == 0; i++) { + + rmem = __find_rmem(s.np); + if (!rmem || !rmem->ops || !rmem->ops->device_init) { + of_node_put(s.np); + continue; + } + + rmem->ops->device_init(rmem, dev, &s); + dev_info(dev, "assigned reserved memory node %s\n", rmem->name); + of_node_put(s.np); + break; + } +} + +/** + * of_reserved_mem_device_release() - release reserved memory device structures + * + * This function releases structures allocated for memory region handling for + * the given device. + */ +void of_reserved_mem_device_release(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct reserved_mem *rmem; + struct of_phandle_args s; + unsigned int i; + + for (i = 0; of_parse_phandle_with_args(np, "memory-region", + "#memory-region-cells", i, &s) == 0; i++) { + + rmem = __find_rmem(s.np); + if (rmem && rmem->ops && rmem->ops->device_release) + rmem->ops->device_release(rmem, dev); + + of_node_put(s.np); + } +} diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 404d1daebefa..3df0b1826e8b 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -21,6 +21,7 @@ #include #include #include +#include #include const struct of_device_id of_default_bus_match_table[] = { @@ -220,6 +221,8 @@ static struct platform_device *of_platform_device_create_pdata( dev->dev.bus = &platform_bus_type; dev->dev.platform_data = platform_data; + of_reserved_mem_device_init(&dev->dev); + /* We do not fill the DMA ops for platform devices by default. * This is currently the responsibility of the platform code * to do such, possibly using a device notifier @@ -227,6 +230,7 @@ static struct platform_device *of_platform_device_create_pdata( if (of_device_add(dev) != 0) { platform_device_put(dev); + of_reserved_mem_device_release(&dev->dev); return NULL; } @@ -282,6 +286,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node, else of_device_make_bus_id(&dev->dev); + of_reserved_mem_device_init(&dev->dev); + /* Allow the HW Peripheral ID to be overridden */ prop = of_get_property(node, "arm,primecell-periphid", NULL); if (prop) @@ -308,6 +314,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node, return dev; err_free: + of_reserved_mem_device_release(&dev->dev); amba_device_put(dev); return NULL; } diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index bc2121fa9132..f10f64fcc815 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -167,6 +167,16 @@ #define CLK_OF_TABLES() #endif +#ifdef CONFIG_OF_RESERVED_MEM +#define RESERVEDMEM_OF_TABLES() \ + . = ALIGN(8); \ + VMLINUX_SYMBOL(__reservedmem_of_table) = .; \ + *(__reservedmem_of_table) \ + *(__reservedmem_of_table_end) +#else +#define RESERVEDMEM_OF_TABLES() +#endif + #define KERNEL_DTB() \ STRUCT_ALIGN(); \ VMLINUX_SYMBOL(__dtb_start) = .; \ @@ -490,6 +500,7 @@ TRACE_SYSCALLS() \ MEM_DISCARD(init.rodata) \ CLK_OF_TABLES() \ + RESERVEDMEM_OF_TABLES() \ CLKSRC_OF_TABLES() \ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE() diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h new file mode 100644 index 000000000000..0bbec4bf23ce --- /dev/null +++ b/include/linux/of_reserved_mem.h @@ -0,0 +1,65 @@ +#ifndef __OF_RESERVED_MEM_H +#define __OF_RESERVED_MEM_H + +struct cma; +struct device; +struct of_phandle_args; +struct reserved_mem_ops; + +struct reserved_mem { + const char *name; + unsigned long fdt_node; + unsigned long phandle; + const struct reserved_mem_ops *ops; + phys_addr_t base; + phys_addr_t size; + void *priv; +}; + +struct reserved_mem_ops { + void (*device_init)(struct reserved_mem *rmem, + struct device *dev, + struct of_phandle_args *args); + void (*device_release)(struct reserved_mem *rmem, + struct device *dev); +}; + +typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem, + unsigned long node, const char *uname); + +#ifdef CONFIG_OF_RESERVED_MEM +void of_reserved_mem_device_init(struct device *dev); +void of_reserved_mem_device_release(struct device *dev); + +void fdt_init_reserved_mem(void); +void early_init_fdt_scan_reserved_mem(void); +int fdt_reserved_mem_save_node(unsigned long node, const char *uname, + phys_addr_t base, phys_addr_t size); + +#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ + static const struct of_device_id __reservedmem_of_table_##name \ + __used __section(__reservedmem_of_table) \ + = { .compatible = compat, \ + .data = (init == (reservedmem_of_init_fn)NULL) ? \ + init : init } + +#else +static inline void of_reserved_mem_device_init(struct device *dev) { } +static inline void of_reserved_mem_device_release(struct device *pdev) { } + +static inline void fdt_init_reserved_mem(void) { } +static inline void early_init_fdt_scan_reserved_mem(void) { } +static inline int +fdt_reserved_mem_save_node(unsigned long node, const char *uname, + phys_addr_t base, phys_addr_t size) { } + +#define RESERVEDMEM_OF_DECLARE(name, compat, init) \ + static const struct of_device_id __reservedmem_of_table_##name \ + __attribute__((unused)) \ + = { .compatible = compat, \ + .data = (init == (reservedmem_of_init_fn)NULL) ? \ + init : init } + +#endif + +#endif /* __OF_RESERVED_MEM_H */