From patchwork Wed Nov 15 17:51:30 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oliver O'Halloran X-Patchwork-Id: 838281 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3ycXDZ4k4gz9sBW for ; Thu, 16 Nov 2017 04:58:38 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="T7b79itT"; dkim-atps=neutral Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3ycXDZ0YSgzDqm7 for ; Thu, 16 Nov 2017 04:58:38 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="T7b79itT"; dkim-atps=neutral X-Original-To: linuxppc-dev@lists.ozlabs.org Delivered-To: linuxppc-dev@lists.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gmail.com (client-ip=2607:f8b0:400e:c05::241; helo=mail-pg0-x241.google.com; envelope-from=oohall@gmail.com; receiver=) Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="T7b79itT"; dkim-atps=neutral Received: from mail-pg0-x241.google.com (mail-pg0-x241.google.com [IPv6:2607:f8b0:400e:c05::241]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3ycX4y1qjfzDqm5 for ; Thu, 16 Nov 2017 04:52:01 +1100 (AEDT) Received: by mail-pg0-x241.google.com with SMTP id s11so13075036pgc.5 for ; Wed, 15 Nov 2017 09:52:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=AhAvSP0sL735wadobdt9rJ+hUYR7seSFwttHHkzS2wo=; b=T7b79itTvd7jeIiOXBbbteP3UgD4QdOGI2E214EKNw7fXSFO8Z+61VRcpUhQSCGiwZ 8Lsnzypb85+xV4za0T/dKttwvLbGMENup0jsS+9xuzxGi6AB87J8OGYN55/l09NOv7S0 Z99jZ/BSneiggX4rshFvxLp4a8HOBjaejYjIOM+PBBdynDonACiu3NDDah7PbwXIckhr bJ39CB4USFonArrPKbQDOIMbswM6tRTOMIjLXF/VEnZIWIdSWSVEPZ37Lb42ly/Kp4OB TBc/aYM0rDKFLCOhKUIc2aAkbfbQfR3TMkM+7/XjrZBTCCem8HOdNwrQ8/8s0nDM01yJ 5gxA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=AhAvSP0sL735wadobdt9rJ+hUYR7seSFwttHHkzS2wo=; b=sKQb0TO2Oa5E8pomQba/BZw0B/SBvWYMfzyW33s47/p6jbhHmGjGZFt/QT3j9I2/HX zWltXyBR+lv29cJhJoeeWwYa+lGar6SJnJEtlZGJU6vBRMXvquMzU9cwmO8uBErRRPOd s8DXsAA1DUWY6IN8/FstSMdpqjpLWz4gCpp0m6QbdjTy9sLyySQqcI7bK/n7fnoCoVlZ W3Ur9JoNUFxFEKhobZ2vIQ7PkvLSI58e2N6Sqo/pxoYjWdk7RGMA2Fu7dBAqunCOq0kw yAqoU2HO6bvehROy6rEjD3lgNxWSUEEdnU7hwt/XIVUuwKQewkZqRHddJmqNbJC2/QVH YW6w== X-Gm-Message-State: AJaThX5tnA0EQTupEeNLiM09lKsn0B1ePSzz/+J7k+dMdTEOMuNp5LO1 xnXV/uV9STZaJWkkemwFb5fUtA== X-Google-Smtp-Source: AGs4zMYtTE0clh62g9MM02cus+wE+XlNWZy/jR/peoef9e9B8FBGAe+ZobQJRaGoreZyUXwfjIGI6Q== X-Received: by 10.159.204.148 with SMTP id t20mr16630711plo.115.1510768319432; Wed, 15 Nov 2017 09:51:59 -0800 (PST) Received: from localhost.localdomain ([103.57.0.128]) by smtp.gmail.com with ESMTPSA id h185sm6995999pfe.171.2017.11.15.09.51.56 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 15 Nov 2017 09:51:58 -0800 (PST) From: Oliver O'Halloran To: linuxppc-dev@lists.ozlabs.org Subject: [PATCH 2/3] libnvdimm: Add a device-tree interface Date: Thu, 16 Nov 2017 04:51:30 +1100 Message-Id: <20171115175131.23631-2-oohall@gmail.com> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20171115175131.23631-1-oohall@gmail.com> References: <20171115175131.23631-1-oohall@gmail.com> X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.24 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, Oliver O'Halloran , linux-nvdimm@lists.01.org Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Sender: "Linuxppc-dev" A fairly bare-bones set of device-tree bindings so libnvdimm can be used on powerpc and other device-tree based platforms. Cc: devicetree@vger.kernel.org Signed-off-by: Oliver O'Halloran --- .../devicetree/bindings/nvdimm/nvdimm-bus.txt | 69 +++++++ MAINTAINERS | 8 + drivers/nvdimm/Kconfig | 10 + drivers/nvdimm/Makefile | 1 + drivers/nvdimm/of_nvdimm.c | 202 +++++++++++++++++++++ 5 files changed, 290 insertions(+) create mode 100644 Documentation/devicetree/bindings/nvdimm/nvdimm-bus.txt create mode 100644 drivers/nvdimm/of_nvdimm.c diff --git a/Documentation/devicetree/bindings/nvdimm/nvdimm-bus.txt b/Documentation/devicetree/bindings/nvdimm/nvdimm-bus.txt new file mode 100644 index 000000000000..491e7c4900ed --- /dev/null +++ b/Documentation/devicetree/bindings/nvdimm/nvdimm-bus.txt @@ -0,0 +1,69 @@ +Device-tree bindings for nonvolatile-memory / NVDIMMs +----------------------------------------------------- + +Non-volatile DIMMs are memory modules used to provide (cacheable) main memory that +retains its contents across power cycles. In more practical terms, they are kind +of storage device where the contents can be accessed by the CPU directly, +rather than indirectly via a storage controller or similar. This can provide +substantial performance improvements for applications designed to take +advantage of in-memory storage. + +This binding provides a way to describe memory regions that should be managed +by an NVDIMM storage driver (libNVDIMM in Linux) and some of the associated +metadata. The binding itself is split into two main parts: A container bus and +its sub-nodes which describe which memory address ranges corresponding to +NVDIMM backed memory. + +Bindings for the container bus: +------------------------------ + +Required properties: + - compatible = "nvdimm-bus"; + - ranges; + A blank ranges property is required because the sub-nodes have + addresses in the system's physical address space. + +The use of a container bus is mainly to handle future expansion of the binding. For +comparison the ACPI equivalent of this binding (NFIT) describes: Memory regions, DIMM +control structures, Block mode DIMM control structures, interleave sets, and more. Some +of these structures cross reference each other so everyone should be happier if we keep +it relatively self contained. + +Bindings for the region nodes: +----------------------------- + +Required properties: + - compatible = "nvdimm-persistent" or "nvdimm-volatile" + + The "nvdimm-persistent" region type indicates that this memory region + is actually a persistent region. The volatile type is mainly useful + for testing and RAM disks that can persist across kexec. + + - reg = ; + The reg property should only contain one address range. + +Optional properties: + - Any relevant NUMA assocativity properties for the target platform. + +A complete example: +-------------------- + +/ { + #size-cells = <1>; + #address-cells = <1>; + + nonvolatile-memory { + compatible = "nonvolatile-memory"; + ranges; + + region@5000 { + compatible = "nvdimm-persistent"; + reg = <0x5000 0x1000>; + }; + + region@6000 { + compatible = "nvdimm-volatile"; + reg = <0x6000 0x1000>; + }; + }; +}; diff --git a/MAINTAINERS b/MAINTAINERS index 65eff7857ec3..0350bf5a94d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7875,6 +7875,14 @@ Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ S: Supported F: drivers/nvdimm/pmem* +LIBNVDIMM: DEVICETREE BINDINGS +M: Oliver O'Halloran +L: linux-nvdimm@lists.01.org +Q: https://patchwork.kernel.org/project/linux-nvdimm/list/ +S: Supported +F: drivers/nvdimm/of_nvdimm.c +F: Documentation/devicetree/bindings/nvdimm/nvdimm-bus.txt + LIBNVDIMM: NON-VOLATILE MEMORY DEVICE SUBSYSTEM M: Dan Williams L: linux-nvdimm@lists.01.org diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig index 5bdd499b5f4f..72d147b55596 100644 --- a/drivers/nvdimm/Kconfig +++ b/drivers/nvdimm/Kconfig @@ -102,4 +102,14 @@ config NVDIMM_DAX Select Y if unsure +config OF_NVDIMM + tristate "Device-tree support for NVDIMMs" + depends on OF + default LIBNVDIMM + help + Allows byte addressable persistent memory regions to be described in the + device-tree. + + Select Y if unsure. + endif diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile index ca6d325438a3..9029511a8486 100644 --- a/drivers/nvdimm/Makefile +++ b/drivers/nvdimm/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_BLK_DEV_PMEM) += nd_pmem.o obj-$(CONFIG_ND_BTT) += nd_btt.o obj-$(CONFIG_ND_BLK) += nd_blk.o obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o +obj-$(CONFIG_OF_NVDIMM) += of_nvdimm.o nd_pmem-y := pmem.o diff --git a/drivers/nvdimm/of_nvdimm.c b/drivers/nvdimm/of_nvdimm.c new file mode 100644 index 000000000000..765cbbae8bcb --- /dev/null +++ b/drivers/nvdimm/of_nvdimm.c @@ -0,0 +1,202 @@ +/* + * Copyright 2017, IBM Corporation + * + * 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 option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can access it online at + * http://www.gnu.org/licenses/gpl-2.0.html. + */ + +#define pr_fmt(fmt) "of_nvdimm: " fmt + +#include +#include +#include +#include +#include +#include + +static const struct attribute_group *region_attr_groups[] = { + &nd_region_attribute_group, + &nd_device_attribute_group, + NULL, +}; + +static int of_nvdimm_add_byte(struct nvdimm_bus *bus, struct device_node *np) +{ + struct nd_region_desc ndr_desc; + struct resource temp_res; + struct nd_region *region; + + /* + * byte regions should only have one address range + */ + if (of_address_to_resource(np, 0, &temp_res)) { + pr_warn("Unable to parse reg[0] for %pOF\n", np); + return -ENXIO; + } + + pr_debug("Found %pR for %pOF\n", &temp_res, np); + + memset(&ndr_desc, 0, sizeof(ndr_desc)); + ndr_desc.res = &temp_res; + ndr_desc.of_node = np; + ndr_desc.attr_groups = region_attr_groups; +#ifdef CONFIG_NUMA + ndr_desc.numa_node = of_node_to_nid(np); +#endif + set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); + + if (of_device_is_compatible(np, "nvdimm-volatile")) + region = nvdimm_volatile_region_create(bus, &ndr_desc); + else + region = nvdimm_pmem_region_create(bus, &ndr_desc); + + if (!region) + return -ENXIO; + + return 0; +} + +static const struct of_device_id of_nvdimm_dev_types[] = { + { .compatible = "nvdimm-persistent", .data = of_nvdimm_add_byte }, + { .compatible = "nvdimm-volatile", .data = of_nvdimm_add_byte }, + { }, +}; + +static void of_nvdimm_parse_one(struct nvdimm_bus *bus, + struct device_node *node) +{ + int (*parse_node)(struct nvdimm_bus *, struct device_node *); + const struct of_device_id *match; + int rc; + + if (of_node_test_and_set_flag(node, OF_POPULATED)) { + pr_debug("%pOF already parsed, skipping\n", node); + return; + } + + match = of_match_node(of_nvdimm_dev_types, node); + if (!match) { + pr_info("No compatible match for '%pOF'\n", node); + of_node_clear_flag(node, OF_POPULATED); + return; + } + + of_node_get(node); + parse_node = match->data; + rc = parse_node(bus, node); + + if (rc) { + of_node_clear_flag(node, OF_POPULATED); + of_node_put(node); + } + + pr_debug("Parsed %pOF, rc = %d\n", node, rc); + + return; +} + +/* + * The nvdimm core refers to the bus descriptor structure at runtime + * so we need to keep it around. Note that that this is different to + * the other nd_*_desc types which are essentially containers for extra + * function parameters. + */ +struct of_nd_private { + struct nvdimm_bus_descriptor desc; + struct nvdimm_bus *bus; +}; + +static const struct attribute_group *bus_attr_groups[] = { + &nvdimm_bus_attribute_group, + NULL, +}; + +static int of_nvdimm_probe(struct platform_device *pdev) +{ + struct device_node *node, *child; + struct of_nd_private *priv; + + node = dev_of_node(&pdev->dev); + if (!node) + return -ENXIO; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->desc.attr_groups = bus_attr_groups; + priv->desc.provider_name = "of_nvdimm"; + priv->desc.module = THIS_MODULE; + priv->desc.of_node = node; + + priv->bus = nvdimm_bus_register(&pdev->dev, &priv->desc); + if (!priv->bus) + goto err; + + platform_set_drvdata(pdev, priv); + + /* now walk the node bus and setup regions, etc */ + for_each_available_child_of_node(node, child) + of_nvdimm_parse_one(priv->bus, child); + + return 0; + +err: + nvdimm_bus_unregister(priv->bus); + kfree(priv); + return -ENXIO; +} + +static int of_nvdimm_remove(struct platform_device *pdev) +{ + struct of_nd_private *priv = platform_get_drvdata(pdev); + struct device_node *node; + + if (!priv) + return 0; /* possible? */ + + for_each_available_child_of_node(pdev->dev.of_node, node) { + if (!of_node_check_flag(node, OF_POPULATED)) + continue; + + of_node_clear_flag(node, OF_POPULATED); + of_node_put(node); + pr_debug("de-populating %s\n", node->full_name); + } + + nvdimm_bus_unregister(priv->bus); + kfree(priv); + + return 0; +} + +static const struct of_device_id of_nvdimm_bus_match[] = { + { .compatible = "nvdimm-bus" }, + { }, +}; + +static const struct platform_driver of_nvdimm_driver = { + .probe = of_nvdimm_probe, + .remove = of_nvdimm_remove, + .driver = { + .name = "of_nvdimm", + .owner = THIS_MODULE, + .of_match_table = of_nvdimm_bus_match, + }, +}; + +module_platform_driver(of_nvdimm_driver); +MODULE_DEVICE_TABLE(of, of_nvdimm_bus_match); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("IBM Corporation");