diff options
author | Sebastian Ott <sebott@linux.vnet.ibm.com> | 2012-08-28 10:46:26 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2012-09-26 09:44:59 -0400 |
commit | 1d1c8f78bed5f8e769757525bd9c2dec69f11a44 (patch) | |
tree | 75f1ef4eb3836975e46a86fad456e8bafa1a5ffe /drivers/s390 | |
parent | 184b08afb5eab8a43d75f4aa0a0f912653f797d7 (diff) |
s390: add scm bus driver
Bus driver to manage Storage Class Memory.
Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/cio/Makefile | 1 | ||||
-rw-r--r-- | drivers/s390/cio/scm.c | 271 |
2 files changed, 272 insertions, 0 deletions
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index e1b700a19648..ac8f210e9a8f 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile | |||
@@ -8,6 +8,7 @@ ccw_device-objs += device.o device_fsm.o device_ops.o | |||
8 | ccw_device-objs += device_id.o device_pgid.o device_status.o | 8 | ccw_device-objs += device_id.o device_pgid.o device_status.o |
9 | obj-y += ccw_device.o cmf.o | 9 | obj-y += ccw_device.o cmf.o |
10 | obj-$(CONFIG_CHSC_SCH) += chsc_sch.o | 10 | obj-$(CONFIG_CHSC_SCH) += chsc_sch.o |
11 | obj-$(CONFIG_SCM_BUS) += scm.o | ||
11 | obj-$(CONFIG_CCWGROUP) += ccwgroup.o | 12 | obj-$(CONFIG_CCWGROUP) += ccwgroup.o |
12 | 13 | ||
13 | qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o | 14 | qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o |
diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c new file mode 100644 index 000000000000..874b64a4bf26 --- /dev/null +++ b/drivers/s390/cio/scm.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
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 | |||
18 | static struct device *scm_root; | ||
19 | static struct eadm_ops *eadm_ops; | ||
20 | static 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 | |||
25 | static 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 | |||
33 | static 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 | |||
41 | static int scmdev_uevent(struct device *dev, struct kobj_uevent_env *env) | ||
42 | { | ||
43 | return add_uevent_var(env, "MODALIAS=scm:scmdev"); | ||
44 | } | ||
45 | |||
46 | static 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 | */ | ||
57 | int 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 | } | ||
65 | EXPORT_SYMBOL_GPL(scm_driver_register); | ||
66 | |||
67 | /** | ||
68 | * scm_driver_unregister() - deregister a scm driver | ||
69 | * @scmdrv: driver to be deregistered | ||
70 | */ | ||
71 | void scm_driver_unregister(struct scm_driver *scmdrv) | ||
72 | { | ||
73 | driver_unregister(&scmdrv->drv); | ||
74 | } | ||
75 | EXPORT_SYMBOL_GPL(scm_driver_unregister); | ||
76 | |||
77 | int 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 | } | ||
88 | EXPORT_SYMBOL_GPL(scm_get_ref); | ||
89 | |||
90 | void scm_put_ref(void) | ||
91 | { | ||
92 | mutex_lock(&eadm_ops_mutex); | ||
93 | module_put(eadm_ops->owner); | ||
94 | mutex_unlock(&eadm_ops_mutex); | ||
95 | } | ||
96 | EXPORT_SYMBOL_GPL(scm_put_ref); | ||
97 | |||
98 | void 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 | } | ||
104 | EXPORT_SYMBOL_GPL(register_eadm_ops); | ||
105 | |||
106 | void 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 | } | ||
112 | EXPORT_SYMBOL_GPL(unregister_eadm_ops); | ||
113 | |||
114 | int scm_start_aob(struct aob *aob) | ||
115 | { | ||
116 | return eadm_ops->eadm_start(aob); | ||
117 | } | ||
118 | EXPORT_SYMBOL_GPL(scm_start_aob); | ||
119 | |||
120 | void 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 | } | ||
128 | EXPORT_SYMBOL_GPL(scm_irq_handler); | ||
129 | |||
130 | #define scm_attr(name) \ | ||
131 | static 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 | } \ | ||
143 | static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL); | ||
144 | |||
145 | scm_attr(persistence); | ||
146 | scm_attr(oper_state); | ||
147 | scm_attr(data_state); | ||
148 | scm_attr(rank); | ||
149 | scm_attr(release); | ||
150 | scm_attr(res_id); | ||
151 | |||
152 | static 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 | |||
162 | static struct attribute_group scmdev_attr_group = { | ||
163 | .attrs = scmdev_attrs, | ||
164 | }; | ||
165 | |||
166 | static const struct attribute_group *scmdev_attr_groups[] = { | ||
167 | &scmdev_attr_group, | ||
168 | NULL, | ||
169 | }; | ||
170 | |||
171 | static void scmdev_release(struct device *dev) | ||
172 | { | ||
173 | struct scm_device *scmdev = to_scm_dev(dev); | ||
174 | |||
175 | kfree(scmdev); | ||
176 | } | ||
177 | |||
178 | static 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 | |||
199 | static int scm_add(struct chsc_scm_info *scm_info, size_t num) | ||
200 | { | ||
201 | struct sale *sale, *scmal = scm_info->scmal; | ||
202 | struct scm_device *scmdev; | ||
203 | int ret; | ||
204 | |||
205 | for (sale = scmal; sale < scmal + num; sale++) { | ||
206 | scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL); | ||
207 | if (!scmdev) | ||
208 | return -ENODEV; | ||
209 | scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc); | ||
210 | ret = device_register(&scmdev->dev); | ||
211 | if (ret) { | ||
212 | /* Release reference from device_initialize(). */ | ||
213 | put_device(&scmdev->dev); | ||
214 | return ret; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static int scm_update_information(void) | ||
222 | { | ||
223 | struct chsc_scm_info *scm_info; | ||
224 | u64 token = 0; | ||
225 | size_t num; | ||
226 | int ret; | ||
227 | |||
228 | scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); | ||
229 | if (!scm_info) | ||
230 | return -ENOMEM; | ||
231 | |||
232 | do { | ||
233 | ret = chsc_scm_info(scm_info, token); | ||
234 | if (ret) | ||
235 | break; | ||
236 | |||
237 | num = (scm_info->response.length - | ||
238 | (offsetof(struct chsc_scm_info, scmal) - | ||
239 | offsetof(struct chsc_scm_info, response)) | ||
240 | ) / sizeof(struct sale); | ||
241 | |||
242 | ret = scm_add(scm_info, num); | ||
243 | if (ret) | ||
244 | break; | ||
245 | |||
246 | token = scm_info->restok; | ||
247 | } while (token); | ||
248 | |||
249 | free_page((unsigned long)scm_info); | ||
250 | |||
251 | return ret; | ||
252 | } | ||
253 | |||
254 | static int __init scm_init(void) | ||
255 | { | ||
256 | int ret; | ||
257 | |||
258 | ret = bus_register(&scm_bus_type); | ||
259 | if (ret) | ||
260 | return ret; | ||
261 | |||
262 | scm_root = root_device_register("scm"); | ||
263 | if (IS_ERR(scm_root)) { | ||
264 | bus_unregister(&scm_bus_type); | ||
265 | return PTR_ERR(scm_root); | ||
266 | } | ||
267 | |||
268 | scm_update_information(); | ||
269 | return 0; | ||
270 | } | ||
271 | subsys_initcall_sync(scm_init); | ||