blob: aa4476e92050bf99576fcd75d9a0ecfa9286a4fb [file] [log] [blame]
Sebastian Ott1d1c8f72012-08-28 16:46:26 +02001/*
2 * Recognize and maintain s390 storage class memory.
3 *
4 * Copyright IBM Corp. 2012
5 * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
6 */
7
8#include <linux/spinlock.h>
9#include <linux/device.h>
10#include <linux/module.h>
11#include <linux/mutex.h>
12#include <linux/slab.h>
13#include <linux/init.h>
14#include <linux/err.h>
15#include <asm/eadm.h>
16#include "chsc.h"
17
18static struct device *scm_root;
19static struct eadm_ops *eadm_ops;
20static DEFINE_MUTEX(eadm_ops_mutex);
21
22#define to_scm_dev(n) container_of(n, struct scm_device, dev)
23#define to_scm_drv(d) container_of(d, struct scm_driver, drv)
24
25static int scmdev_probe(struct device *dev)
26{
27 struct scm_device *scmdev = to_scm_dev(dev);
28 struct scm_driver *scmdrv = to_scm_drv(dev->driver);
29
30 return scmdrv->probe ? scmdrv->probe(scmdev) : -ENODEV;
31}
32
33static int scmdev_remove(struct device *dev)
34{
35 struct scm_device *scmdev = to_scm_dev(dev);
36 struct scm_driver *scmdrv = to_scm_drv(dev->driver);
37
38 return scmdrv->remove ? scmdrv->remove(scmdev) : -ENODEV;
39}
40
41static int scmdev_uevent(struct device *dev, struct kobj_uevent_env *env)
42{
43 return add_uevent_var(env, "MODALIAS=scm:scmdev");
44}
45
46static struct bus_type scm_bus_type = {
47 .name = "scm",
48 .probe = scmdev_probe,
49 .remove = scmdev_remove,
50 .uevent = scmdev_uevent,
51};
52
53/**
54 * scm_driver_register() - register a scm driver
55 * @scmdrv: driver to be registered
56 */
57int scm_driver_register(struct scm_driver *scmdrv)
58{
59 struct device_driver *drv = &scmdrv->drv;
60
61 drv->bus = &scm_bus_type;
62
63 return driver_register(drv);
64}
65EXPORT_SYMBOL_GPL(scm_driver_register);
66
67/**
68 * scm_driver_unregister() - deregister a scm driver
69 * @scmdrv: driver to be deregistered
70 */
71void scm_driver_unregister(struct scm_driver *scmdrv)
72{
73 driver_unregister(&scmdrv->drv);
74}
75EXPORT_SYMBOL_GPL(scm_driver_unregister);
76
77int scm_get_ref(void)
78{
79 int ret = 0;
80
81 mutex_lock(&eadm_ops_mutex);
82 if (!eadm_ops || !try_module_get(eadm_ops->owner))
83 ret = -ENOENT;
84 mutex_unlock(&eadm_ops_mutex);
85
86 return ret;
87}
88EXPORT_SYMBOL_GPL(scm_get_ref);
89
90void scm_put_ref(void)
91{
92 mutex_lock(&eadm_ops_mutex);
93 module_put(eadm_ops->owner);
94 mutex_unlock(&eadm_ops_mutex);
95}
96EXPORT_SYMBOL_GPL(scm_put_ref);
97
98void register_eadm_ops(struct eadm_ops *ops)
99{
100 mutex_lock(&eadm_ops_mutex);
101 eadm_ops = ops;
102 mutex_unlock(&eadm_ops_mutex);
103}
104EXPORT_SYMBOL_GPL(register_eadm_ops);
105
106void unregister_eadm_ops(struct eadm_ops *ops)
107{
108 mutex_lock(&eadm_ops_mutex);
109 eadm_ops = NULL;
110 mutex_unlock(&eadm_ops_mutex);
111}
112EXPORT_SYMBOL_GPL(unregister_eadm_ops);
113
114int scm_start_aob(struct aob *aob)
115{
116 return eadm_ops->eadm_start(aob);
117}
118EXPORT_SYMBOL_GPL(scm_start_aob);
119
120void scm_irq_handler(struct aob *aob, int error)
121{
122 struct aob_rq_header *aobrq = (void *) aob->request.data;
123 struct scm_device *scmdev = aobrq->scmdev;
124 struct scm_driver *scmdrv = to_scm_drv(scmdev->dev.driver);
125
126 scmdrv->handler(scmdev, aobrq->data, error);
127}
128EXPORT_SYMBOL_GPL(scm_irq_handler);
129
130#define scm_attr(name) \
131static ssize_t show_##name(struct device *dev, \
132 struct device_attribute *attr, char *buf) \
133{ \
134 struct scm_device *scmdev = to_scm_dev(dev); \
135 int ret; \
136 \
137 spin_lock(&scmdev->lock); \
138 ret = sprintf(buf, "%u\n", scmdev->attrs.name); \
139 spin_unlock(&scmdev->lock); \
140 \
141 return ret; \
142} \
143static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
144
145scm_attr(persistence);
146scm_attr(oper_state);
147scm_attr(data_state);
148scm_attr(rank);
149scm_attr(release);
150scm_attr(res_id);
151
152static struct attribute *scmdev_attrs[] = {
153 &dev_attr_persistence.attr,
154 &dev_attr_oper_state.attr,
155 &dev_attr_data_state.attr,
156 &dev_attr_rank.attr,
157 &dev_attr_release.attr,
158 &dev_attr_res_id.attr,
159 NULL,
160};
161
162static struct attribute_group scmdev_attr_group = {
163 .attrs = scmdev_attrs,
164};
165
166static const struct attribute_group *scmdev_attr_groups[] = {
167 &scmdev_attr_group,
168 NULL,
169};
170
171static void scmdev_release(struct device *dev)
172{
173 struct scm_device *scmdev = to_scm_dev(dev);
174
175 kfree(scmdev);
176}
177
178static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
179 unsigned int size, unsigned int max_blk_count)
180{
181 dev_set_name(&scmdev->dev, "%016llx", (unsigned long long) sale->sa);
182 scmdev->nr_max_block = max_blk_count;
183 scmdev->address = sale->sa;
184 scmdev->size = 1UL << size;
185 scmdev->attrs.rank = sale->rank;
186 scmdev->attrs.persistence = sale->p;
187 scmdev->attrs.oper_state = sale->op_state;
188 scmdev->attrs.data_state = sale->data_state;
189 scmdev->attrs.rank = sale->rank;
190 scmdev->attrs.release = sale->r;
191 scmdev->attrs.res_id = sale->rid;
192 scmdev->dev.parent = scm_root;
193 scmdev->dev.bus = &scm_bus_type;
194 scmdev->dev.release = scmdev_release;
195 scmdev->dev.groups = scmdev_attr_groups;
196 spin_lock_init(&scmdev->lock);
197}
198
Sebastian Ott40ff4cc2012-08-28 16:47:02 +0200199/*
200 * Check for state-changes, notify the driver and userspace.
201 */
202static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
203{
204 struct scm_driver *scmdrv;
205 bool changed;
206
207 device_lock(&scmdev->dev);
208 changed = scmdev->attrs.rank != sale->rank ||
209 scmdev->attrs.oper_state != sale->op_state;
210 scmdev->attrs.rank = sale->rank;
211 scmdev->attrs.oper_state = sale->op_state;
212 if (!scmdev->dev.driver)
213 goto out;
214 scmdrv = to_scm_drv(scmdev->dev.driver);
215 if (changed && scmdrv->notify)
216 scmdrv->notify(scmdev);
217out:
218 device_unlock(&scmdev->dev);
219 if (changed)
220 kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
221}
222
223static int check_address(struct device *dev, void *data)
224{
225 struct scm_device *scmdev = to_scm_dev(dev);
226 struct sale *sale = data;
227
228 return scmdev->address == sale->sa;
229}
230
231static struct scm_device *scmdev_find(struct sale *sale)
232{
233 struct device *dev;
234
235 dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);
236
237 return dev ? to_scm_dev(dev) : NULL;
238}
239
Sebastian Ott1d1c8f72012-08-28 16:46:26 +0200240static int scm_add(struct chsc_scm_info *scm_info, size_t num)
241{
242 struct sale *sale, *scmal = scm_info->scmal;
243 struct scm_device *scmdev;
244 int ret;
245
246 for (sale = scmal; sale < scmal + num; sale++) {
Sebastian Ott40ff4cc2012-08-28 16:47:02 +0200247 scmdev = scmdev_find(sale);
248 if (scmdev) {
249 scmdev_update(scmdev, sale);
250 /* Release reference from scm_find(). */
251 put_device(&scmdev->dev);
252 continue;
253 }
Sebastian Ott1d1c8f72012-08-28 16:46:26 +0200254 scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
255 if (!scmdev)
256 return -ENODEV;
257 scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc);
258 ret = device_register(&scmdev->dev);
259 if (ret) {
260 /* Release reference from device_initialize(). */
261 put_device(&scmdev->dev);
262 return ret;
263 }
264 }
265
266 return 0;
267}
268
Sebastian Ott40ff4cc2012-08-28 16:47:02 +0200269int scm_update_information(void)
Sebastian Ott1d1c8f72012-08-28 16:46:26 +0200270{
271 struct chsc_scm_info *scm_info;
272 u64 token = 0;
273 size_t num;
274 int ret;
275
276 scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
277 if (!scm_info)
278 return -ENOMEM;
279
280 do {
281 ret = chsc_scm_info(scm_info, token);
282 if (ret)
283 break;
284
285 num = (scm_info->response.length -
286 (offsetof(struct chsc_scm_info, scmal) -
287 offsetof(struct chsc_scm_info, response))
288 ) / sizeof(struct sale);
289
290 ret = scm_add(scm_info, num);
291 if (ret)
292 break;
293
294 token = scm_info->restok;
295 } while (token);
296
297 free_page((unsigned long)scm_info);
298
299 return ret;
300}
301
302static int __init scm_init(void)
303{
304 int ret;
305
306 ret = bus_register(&scm_bus_type);
307 if (ret)
308 return ret;
309
310 scm_root = root_device_register("scm");
311 if (IS_ERR(scm_root)) {
312 bus_unregister(&scm_bus_type);
313 return PTR_ERR(scm_root);
314 }
315
316 scm_update_information();
317 return 0;
318}
319subsys_initcall_sync(scm_init);