blob: 1ce159095c52387dec5fbc972c7960c185fb5390 [file] [log] [blame]
Dan Williamsb94d5232015-05-19 22:54:31 -04001/*
2 * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 */
13#include <linux/libnvdimm.h>
14#include <linux/export.h>
15#include <linux/module.h>
16#include <linux/device.h>
Dan Williams62232e452015-06-08 14:27:06 -040017#include <linux/ndctl.h>
Dan Williams45def222015-04-26 19:26:48 -040018#include <linux/mutex.h>
Dan Williamsb94d5232015-05-19 22:54:31 -040019#include <linux/slab.h>
20#include "nd-core.h"
21
Dan Williamse6dfb2d2015-04-25 03:56:17 -040022LIST_HEAD(nvdimm_bus_list);
23DEFINE_MUTEX(nvdimm_bus_list_mutex);
Dan Williamsb94d5232015-05-19 22:54:31 -040024static DEFINE_IDA(nd_ida);
25
26static void nvdimm_bus_release(struct device *dev)
27{
28 struct nvdimm_bus *nvdimm_bus;
29
30 nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
31 ida_simple_remove(&nd_ida, nvdimm_bus->id);
32 kfree(nvdimm_bus);
33}
34
Dan Williams45def222015-04-26 19:26:48 -040035struct nvdimm_bus *to_nvdimm_bus(struct device *dev)
36{
37 struct nvdimm_bus *nvdimm_bus;
38
39 nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
40 WARN_ON(nvdimm_bus->dev.release != nvdimm_bus_release);
41 return nvdimm_bus;
42}
43EXPORT_SYMBOL_GPL(to_nvdimm_bus);
44
45struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus)
46{
47 /* struct nvdimm_bus definition is private to libnvdimm */
48 return nvdimm_bus->nd_desc;
49}
50EXPORT_SYMBOL_GPL(to_nd_desc);
51
Dan Williamse6dfb2d2015-04-25 03:56:17 -040052struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev)
53{
54 struct device *dev;
55
56 for (dev = nd_dev; dev; dev = dev->parent)
57 if (dev->release == nvdimm_bus_release)
58 break;
59 dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n");
60 if (dev)
61 return to_nvdimm_bus(dev);
62 return NULL;
63}
64
Dan Williams62232e452015-06-08 14:27:06 -040065static ssize_t commands_show(struct device *dev,
66 struct device_attribute *attr, char *buf)
67{
68 int cmd, len = 0;
69 struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
70 struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
71
72 for_each_set_bit(cmd, &nd_desc->dsm_mask, BITS_PER_LONG)
73 len += sprintf(buf + len, "%s ", nvdimm_bus_cmd_name(cmd));
74 len += sprintf(buf + len, "\n");
75 return len;
76}
77static DEVICE_ATTR_RO(commands);
78
Dan Williams45def222015-04-26 19:26:48 -040079static const char *nvdimm_bus_provider(struct nvdimm_bus *nvdimm_bus)
80{
81 struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
82 struct device *parent = nvdimm_bus->dev.parent;
83
84 if (nd_desc->provider_name)
85 return nd_desc->provider_name;
86 else if (parent)
87 return dev_name(parent);
88 else
89 return "unknown";
90}
91
92static ssize_t provider_show(struct device *dev,
93 struct device_attribute *attr, char *buf)
94{
95 struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
96
97 return sprintf(buf, "%s\n", nvdimm_bus_provider(nvdimm_bus));
98}
99static DEVICE_ATTR_RO(provider);
100
101static struct attribute *nvdimm_bus_attributes[] = {
Dan Williams62232e452015-06-08 14:27:06 -0400102 &dev_attr_commands.attr,
Dan Williams45def222015-04-26 19:26:48 -0400103 &dev_attr_provider.attr,
104 NULL,
105};
106
107struct attribute_group nvdimm_bus_attribute_group = {
108 .attrs = nvdimm_bus_attributes,
109};
110EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group);
111
Dan Williamsb94d5232015-05-19 22:54:31 -0400112struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
113 struct nvdimm_bus_descriptor *nd_desc)
114{
115 struct nvdimm_bus *nvdimm_bus;
116 int rc;
117
118 nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL);
119 if (!nvdimm_bus)
120 return NULL;
Dan Williams45def222015-04-26 19:26:48 -0400121 INIT_LIST_HEAD(&nvdimm_bus->list);
Dan Williamsb94d5232015-05-19 22:54:31 -0400122 nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
123 if (nvdimm_bus->id < 0) {
124 kfree(nvdimm_bus);
125 return NULL;
126 }
127 nvdimm_bus->nd_desc = nd_desc;
128 nvdimm_bus->dev.parent = parent;
129 nvdimm_bus->dev.release = nvdimm_bus_release;
Dan Williams45def222015-04-26 19:26:48 -0400130 nvdimm_bus->dev.groups = nd_desc->attr_groups;
Dan Williamsb94d5232015-05-19 22:54:31 -0400131 dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id);
132 rc = device_register(&nvdimm_bus->dev);
133 if (rc) {
134 dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc);
Dan Williams45def222015-04-26 19:26:48 -0400135 goto err;
Dan Williamsb94d5232015-05-19 22:54:31 -0400136 }
137
Dan Williams45def222015-04-26 19:26:48 -0400138 rc = nvdimm_bus_create_ndctl(nvdimm_bus);
139 if (rc)
140 goto err;
141
142 mutex_lock(&nvdimm_bus_list_mutex);
143 list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list);
144 mutex_unlock(&nvdimm_bus_list_mutex);
145
Dan Williamsb94d5232015-05-19 22:54:31 -0400146 return nvdimm_bus;
Dan Williams45def222015-04-26 19:26:48 -0400147 err:
148 put_device(&nvdimm_bus->dev);
149 return NULL;
Dan Williamsb94d5232015-05-19 22:54:31 -0400150}
151EXPORT_SYMBOL_GPL(nvdimm_bus_register);
152
Dan Williamse6dfb2d2015-04-25 03:56:17 -0400153static int child_unregister(struct device *dev, void *data)
154{
155 /*
156 * the singular ndctl class device per bus needs to be
157 * "device_destroy"ed, so skip it here
158 *
159 * i.e. remove classless children
160 */
161 if (dev->class)
162 /* pass */;
163 else
164 device_unregister(dev);
165 return 0;
166}
167
Dan Williamsb94d5232015-05-19 22:54:31 -0400168void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
169{
170 if (!nvdimm_bus)
171 return;
Dan Williams45def222015-04-26 19:26:48 -0400172
173 mutex_lock(&nvdimm_bus_list_mutex);
174 list_del_init(&nvdimm_bus->list);
175 mutex_unlock(&nvdimm_bus_list_mutex);
176
Dan Williamse6dfb2d2015-04-25 03:56:17 -0400177 device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister);
Dan Williams45def222015-04-26 19:26:48 -0400178 nvdimm_bus_destroy_ndctl(nvdimm_bus);
179
Dan Williamsb94d5232015-05-19 22:54:31 -0400180 device_unregister(&nvdimm_bus->dev);
181}
182EXPORT_SYMBOL_GPL(nvdimm_bus_unregister);
183
Dan Williams45def222015-04-26 19:26:48 -0400184static __init int libnvdimm_init(void)
185{
186 return nvdimm_bus_init();
187}
188
189static __exit void libnvdimm_exit(void)
190{
191 WARN_ON(!list_empty(&nvdimm_bus_list));
192 nvdimm_bus_exit();
193}
194
Dan Williamsb94d5232015-05-19 22:54:31 -0400195MODULE_LICENSE("GPL v2");
196MODULE_AUTHOR("Intel Corporation");
Dan Williams45def222015-04-26 19:26:48 -0400197subsys_initcall(libnvdimm_init);
198module_exit(libnvdimm_exit);