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