18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright(c) 2013-2016 Intel Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/device.h>
68c2ecf20Sopenharmony_ci#include <linux/sizes.h>
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci#include <linux/mm.h>
98c2ecf20Sopenharmony_ci#include "nd-core.h"
108c2ecf20Sopenharmony_ci#include "pfn.h"
118c2ecf20Sopenharmony_ci#include "nd.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic void nd_dax_release(struct device *dev)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	struct nd_region *nd_region = to_nd_region(dev->parent);
168c2ecf20Sopenharmony_ci	struct nd_dax *nd_dax = to_nd_dax(dev);
178c2ecf20Sopenharmony_ci	struct nd_pfn *nd_pfn = &nd_dax->nd_pfn;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	dev_dbg(dev, "trace\n");
208c2ecf20Sopenharmony_ci	nd_detach_ndns(dev, &nd_pfn->ndns);
218c2ecf20Sopenharmony_ci	ida_simple_remove(&nd_region->dax_ida, nd_pfn->id);
228c2ecf20Sopenharmony_ci	kfree(nd_pfn->uuid);
238c2ecf20Sopenharmony_ci	kfree(nd_dax);
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct nd_dax *to_nd_dax(struct device *dev)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct nd_dax *nd_dax = container_of(dev, struct nd_dax, nd_pfn.dev);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	WARN_ON(!is_nd_dax(dev));
318c2ecf20Sopenharmony_ci	return nd_dax;
328c2ecf20Sopenharmony_ci}
338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(to_nd_dax);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic const struct device_type nd_dax_device_type = {
368c2ecf20Sopenharmony_ci	.name = "nd_dax",
378c2ecf20Sopenharmony_ci	.release = nd_dax_release,
388c2ecf20Sopenharmony_ci	.groups = nd_pfn_attribute_groups,
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cibool is_nd_dax(struct device *dev)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return dev ? dev->type == &nd_dax_device_type : false;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(is_nd_dax);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic struct nd_dax *nd_dax_alloc(struct nd_region *nd_region)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct nd_pfn *nd_pfn;
508c2ecf20Sopenharmony_ci	struct nd_dax *nd_dax;
518c2ecf20Sopenharmony_ci	struct device *dev;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	nd_dax = kzalloc(sizeof(*nd_dax), GFP_KERNEL);
548c2ecf20Sopenharmony_ci	if (!nd_dax)
558c2ecf20Sopenharmony_ci		return NULL;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	nd_pfn = &nd_dax->nd_pfn;
588c2ecf20Sopenharmony_ci	nd_pfn->id = ida_simple_get(&nd_region->dax_ida, 0, 0, GFP_KERNEL);
598c2ecf20Sopenharmony_ci	if (nd_pfn->id < 0) {
608c2ecf20Sopenharmony_ci		kfree(nd_dax);
618c2ecf20Sopenharmony_ci		return NULL;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	dev = &nd_pfn->dev;
658c2ecf20Sopenharmony_ci	dev_set_name(dev, "dax%d.%d", nd_region->id, nd_pfn->id);
668c2ecf20Sopenharmony_ci	dev->type = &nd_dax_device_type;
678c2ecf20Sopenharmony_ci	dev->parent = &nd_region->dev;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return nd_dax;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistruct device *nd_dax_create(struct nd_region *nd_region)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct device *dev = NULL;
758c2ecf20Sopenharmony_ci	struct nd_dax *nd_dax;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (!is_memory(&nd_region->dev))
788c2ecf20Sopenharmony_ci		return NULL;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	nd_dax = nd_dax_alloc(nd_region);
818c2ecf20Sopenharmony_ci	if (nd_dax)
828c2ecf20Sopenharmony_ci		dev = nd_pfn_devinit(&nd_dax->nd_pfn, NULL);
838c2ecf20Sopenharmony_ci	__nd_device_register(dev);
848c2ecf20Sopenharmony_ci	return dev;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ciint nd_dax_probe(struct device *dev, struct nd_namespace_common *ndns)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	int rc;
908c2ecf20Sopenharmony_ci	struct nd_dax *nd_dax;
918c2ecf20Sopenharmony_ci	struct device *dax_dev;
928c2ecf20Sopenharmony_ci	struct nd_pfn *nd_pfn;
938c2ecf20Sopenharmony_ci	struct nd_pfn_sb *pfn_sb;
948c2ecf20Sopenharmony_ci	struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (ndns->force_raw)
978c2ecf20Sopenharmony_ci		return -ENODEV;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	switch (ndns->claim_class) {
1008c2ecf20Sopenharmony_ci	case NVDIMM_CCLASS_NONE:
1018c2ecf20Sopenharmony_ci	case NVDIMM_CCLASS_DAX:
1028c2ecf20Sopenharmony_ci		break;
1038c2ecf20Sopenharmony_ci	default:
1048c2ecf20Sopenharmony_ci		return -ENODEV;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	nvdimm_bus_lock(&ndns->dev);
1088c2ecf20Sopenharmony_ci	nd_dax = nd_dax_alloc(nd_region);
1098c2ecf20Sopenharmony_ci	nd_pfn = &nd_dax->nd_pfn;
1108c2ecf20Sopenharmony_ci	dax_dev = nd_pfn_devinit(nd_pfn, ndns);
1118c2ecf20Sopenharmony_ci	nvdimm_bus_unlock(&ndns->dev);
1128c2ecf20Sopenharmony_ci	if (!dax_dev)
1138c2ecf20Sopenharmony_ci		return -ENOMEM;
1148c2ecf20Sopenharmony_ci	pfn_sb = devm_kmalloc(dev, sizeof(*pfn_sb), GFP_KERNEL);
1158c2ecf20Sopenharmony_ci	nd_pfn->pfn_sb = pfn_sb;
1168c2ecf20Sopenharmony_ci	rc = nd_pfn_validate(nd_pfn, DAX_SIG);
1178c2ecf20Sopenharmony_ci	dev_dbg(dev, "dax: %s\n", rc == 0 ? dev_name(dax_dev) : "<none>");
1188c2ecf20Sopenharmony_ci	if (rc < 0) {
1198c2ecf20Sopenharmony_ci		nd_detach_ndns(dax_dev, &nd_pfn->ndns);
1208c2ecf20Sopenharmony_ci		put_device(dax_dev);
1218c2ecf20Sopenharmony_ci	} else
1228c2ecf20Sopenharmony_ci		__nd_device_register(dax_dev);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return rc;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nd_dax_probe);
127