xref: /openbmc/linux/drivers/nvdimm/region.c (revision 3d88002e)
13d88002eSDan Williams /*
23d88002eSDan Williams  * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
33d88002eSDan Williams  *
43d88002eSDan Williams  * This program is free software; you can redistribute it and/or modify
53d88002eSDan Williams  * it under the terms of version 2 of the GNU General Public License as
63d88002eSDan Williams  * published by the Free Software Foundation.
73d88002eSDan Williams  *
83d88002eSDan Williams  * This program is distributed in the hope that it will be useful, but
93d88002eSDan Williams  * WITHOUT ANY WARRANTY; without even the implied warranty of
103d88002eSDan Williams  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
113d88002eSDan Williams  * General Public License for more details.
123d88002eSDan Williams  */
133d88002eSDan Williams #include <linux/module.h>
143d88002eSDan Williams #include <linux/device.h>
153d88002eSDan Williams #include <linux/nd.h>
163d88002eSDan Williams #include "nd.h"
173d88002eSDan Williams 
183d88002eSDan Williams static int nd_region_probe(struct device *dev)
193d88002eSDan Williams {
203d88002eSDan Williams 	int err;
213d88002eSDan Williams 	struct nd_region_namespaces *num_ns;
223d88002eSDan Williams 	struct nd_region *nd_region = to_nd_region(dev);
233d88002eSDan Williams 	int rc = nd_region_register_namespaces(nd_region, &err);
243d88002eSDan Williams 
253d88002eSDan Williams 	num_ns = devm_kzalloc(dev, sizeof(*num_ns), GFP_KERNEL);
263d88002eSDan Williams 	if (!num_ns)
273d88002eSDan Williams 		return -ENOMEM;
283d88002eSDan Williams 
293d88002eSDan Williams 	if (rc < 0)
303d88002eSDan Williams 		return rc;
313d88002eSDan Williams 
323d88002eSDan Williams 	num_ns->active = rc;
333d88002eSDan Williams 	num_ns->count = rc + err;
343d88002eSDan Williams 	dev_set_drvdata(dev, num_ns);
353d88002eSDan Williams 
363d88002eSDan Williams 	if (err == 0)
373d88002eSDan Williams 		return 0;
383d88002eSDan Williams 
393d88002eSDan Williams 	if (rc == err)
403d88002eSDan Williams 		return -ENODEV;
413d88002eSDan Williams 
423d88002eSDan Williams 	/*
433d88002eSDan Williams 	 * Given multiple namespaces per region, we do not want to
443d88002eSDan Williams 	 * disable all the successfully registered peer namespaces upon
453d88002eSDan Williams 	 * a single registration failure.  If userspace is missing a
463d88002eSDan Williams 	 * namespace that it expects it can disable/re-enable the region
473d88002eSDan Williams 	 * to retry discovery after correcting the failure.
483d88002eSDan Williams 	 * <regionX>/namespaces returns the current
493d88002eSDan Williams 	 * "<async-registered>/<total>" namespace count.
503d88002eSDan Williams 	 */
513d88002eSDan Williams 	dev_err(dev, "failed to register %d namespace%s, continuing...\n",
523d88002eSDan Williams 			err, err == 1 ? "" : "s");
533d88002eSDan Williams 	return 0;
543d88002eSDan Williams }
553d88002eSDan Williams 
563d88002eSDan Williams static int child_unregister(struct device *dev, void *data)
573d88002eSDan Williams {
583d88002eSDan Williams 	nd_device_unregister(dev, ND_SYNC);
593d88002eSDan Williams 	return 0;
603d88002eSDan Williams }
613d88002eSDan Williams 
623d88002eSDan Williams static int nd_region_remove(struct device *dev)
633d88002eSDan Williams {
643d88002eSDan Williams 	/* flush attribute readers and disable */
653d88002eSDan Williams 	nvdimm_bus_lock(dev);
663d88002eSDan Williams 	dev_set_drvdata(dev, NULL);
673d88002eSDan Williams 	nvdimm_bus_unlock(dev);
683d88002eSDan Williams 
693d88002eSDan Williams 	device_for_each_child(dev, NULL, child_unregister);
703d88002eSDan Williams 	return 0;
713d88002eSDan Williams }
723d88002eSDan Williams 
733d88002eSDan Williams static struct nd_device_driver nd_region_driver = {
743d88002eSDan Williams 	.probe = nd_region_probe,
753d88002eSDan Williams 	.remove = nd_region_remove,
763d88002eSDan Williams 	.drv = {
773d88002eSDan Williams 		.name = "nd_region",
783d88002eSDan Williams 	},
793d88002eSDan Williams 	.type = ND_DRIVER_REGION_BLK | ND_DRIVER_REGION_PMEM,
803d88002eSDan Williams };
813d88002eSDan Williams 
823d88002eSDan Williams int __init nd_region_init(void)
833d88002eSDan Williams {
843d88002eSDan Williams 	return nd_driver_register(&nd_region_driver);
853d88002eSDan Williams }
863d88002eSDan Williams 
873d88002eSDan Williams void nd_region_exit(void)
883d88002eSDan Williams {
893d88002eSDan Williams 	driver_unregister(&nd_region_driver.drv);
903d88002eSDan Williams }
913d88002eSDan Williams 
923d88002eSDan Williams MODULE_ALIAS_ND_DEVICE(ND_DEVICE_REGION_PMEM);
933d88002eSDan Williams MODULE_ALIAS_ND_DEVICE(ND_DEVICE_REGION_BLK);
94