xref: /openbmc/linux/drivers/s390/cio/scm.c (revision 724117b7)
1724117b7SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21d1c8f78SSebastian Ott /*
31d1c8f78SSebastian Ott  * Recognize and maintain s390 storage class memory.
41d1c8f78SSebastian Ott  *
51d1c8f78SSebastian Ott  * Copyright IBM Corp. 2012
61d1c8f78SSebastian Ott  * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
71d1c8f78SSebastian Ott  */
81d1c8f78SSebastian Ott 
91d1c8f78SSebastian Ott #include <linux/device.h>
101d1c8f78SSebastian Ott #include <linux/module.h>
111d1c8f78SSebastian Ott #include <linux/mutex.h>
121d1c8f78SSebastian Ott #include <linux/slab.h>
131d1c8f78SSebastian Ott #include <linux/init.h>
141d1c8f78SSebastian Ott #include <linux/err.h>
151d1c8f78SSebastian Ott #include <asm/eadm.h>
161d1c8f78SSebastian Ott #include "chsc.h"
171d1c8f78SSebastian Ott 
181d1c8f78SSebastian Ott static struct device *scm_root;
191d1c8f78SSebastian Ott 
201d1c8f78SSebastian Ott #define to_scm_dev(n) container_of(n, struct scm_device, dev)
211d1c8f78SSebastian Ott #define	to_scm_drv(d) container_of(d, struct scm_driver, drv)
221d1c8f78SSebastian Ott 
231d1c8f78SSebastian Ott static int scmdev_probe(struct device *dev)
241d1c8f78SSebastian Ott {
251d1c8f78SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);
261d1c8f78SSebastian Ott 	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
271d1c8f78SSebastian Ott 
281d1c8f78SSebastian Ott 	return scmdrv->probe ? scmdrv->probe(scmdev) : -ENODEV;
291d1c8f78SSebastian Ott }
301d1c8f78SSebastian Ott 
311d1c8f78SSebastian Ott static int scmdev_remove(struct device *dev)
321d1c8f78SSebastian Ott {
331d1c8f78SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);
341d1c8f78SSebastian Ott 	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
351d1c8f78SSebastian Ott 
361d1c8f78SSebastian Ott 	return scmdrv->remove ? scmdrv->remove(scmdev) : -ENODEV;
371d1c8f78SSebastian Ott }
381d1c8f78SSebastian Ott 
391d1c8f78SSebastian Ott static int scmdev_uevent(struct device *dev, struct kobj_uevent_env *env)
401d1c8f78SSebastian Ott {
411d1c8f78SSebastian Ott 	return add_uevent_var(env, "MODALIAS=scm:scmdev");
421d1c8f78SSebastian Ott }
431d1c8f78SSebastian Ott 
441d1c8f78SSebastian Ott static struct bus_type scm_bus_type = {
451d1c8f78SSebastian Ott 	.name  = "scm",
461d1c8f78SSebastian Ott 	.probe = scmdev_probe,
471d1c8f78SSebastian Ott 	.remove = scmdev_remove,
481d1c8f78SSebastian Ott 	.uevent = scmdev_uevent,
491d1c8f78SSebastian Ott };
501d1c8f78SSebastian Ott 
511d1c8f78SSebastian Ott /**
521d1c8f78SSebastian Ott  * scm_driver_register() - register a scm driver
531d1c8f78SSebastian Ott  * @scmdrv: driver to be registered
541d1c8f78SSebastian Ott  */
551d1c8f78SSebastian Ott int scm_driver_register(struct scm_driver *scmdrv)
561d1c8f78SSebastian Ott {
571d1c8f78SSebastian Ott 	struct device_driver *drv = &scmdrv->drv;
581d1c8f78SSebastian Ott 
591d1c8f78SSebastian Ott 	drv->bus = &scm_bus_type;
601d1c8f78SSebastian Ott 
611d1c8f78SSebastian Ott 	return driver_register(drv);
621d1c8f78SSebastian Ott }
631d1c8f78SSebastian Ott EXPORT_SYMBOL_GPL(scm_driver_register);
641d1c8f78SSebastian Ott 
651d1c8f78SSebastian Ott /**
661d1c8f78SSebastian Ott  * scm_driver_unregister() - deregister a scm driver
671d1c8f78SSebastian Ott  * @scmdrv: driver to be deregistered
681d1c8f78SSebastian Ott  */
691d1c8f78SSebastian Ott void scm_driver_unregister(struct scm_driver *scmdrv)
701d1c8f78SSebastian Ott {
711d1c8f78SSebastian Ott 	driver_unregister(&scmdrv->drv);
721d1c8f78SSebastian Ott }
731d1c8f78SSebastian Ott EXPORT_SYMBOL_GPL(scm_driver_unregister);
741d1c8f78SSebastian Ott 
752a842acaSChristoph Hellwig void scm_irq_handler(struct aob *aob, blk_status_t error)
761d1c8f78SSebastian Ott {
771d1c8f78SSebastian Ott 	struct aob_rq_header *aobrq = (void *) aob->request.data;
781d1c8f78SSebastian Ott 	struct scm_device *scmdev = aobrq->scmdev;
791d1c8f78SSebastian Ott 	struct scm_driver *scmdrv = to_scm_drv(scmdev->dev.driver);
801d1c8f78SSebastian Ott 
811d1c8f78SSebastian Ott 	scmdrv->handler(scmdev, aobrq->data, error);
821d1c8f78SSebastian Ott }
831d1c8f78SSebastian Ott EXPORT_SYMBOL_GPL(scm_irq_handler);
841d1c8f78SSebastian Ott 
851d1c8f78SSebastian Ott #define scm_attr(name)							\
861d1c8f78SSebastian Ott static ssize_t show_##name(struct device *dev,				\
871d1c8f78SSebastian Ott 	       struct device_attribute *attr, char *buf)		\
881d1c8f78SSebastian Ott {									\
891d1c8f78SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);			\
901d1c8f78SSebastian Ott 	int ret;							\
911d1c8f78SSebastian Ott 									\
92c3e6d407SSebastian Ott 	device_lock(dev);						\
931d1c8f78SSebastian Ott 	ret = sprintf(buf, "%u\n", scmdev->attrs.name);			\
94c3e6d407SSebastian Ott 	device_unlock(dev);						\
951d1c8f78SSebastian Ott 									\
961d1c8f78SSebastian Ott 	return ret;							\
971d1c8f78SSebastian Ott }									\
981d1c8f78SSebastian Ott static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
991d1c8f78SSebastian Ott 
1001d1c8f78SSebastian Ott scm_attr(persistence);
1011d1c8f78SSebastian Ott scm_attr(oper_state);
1021d1c8f78SSebastian Ott scm_attr(data_state);
1031d1c8f78SSebastian Ott scm_attr(rank);
1041d1c8f78SSebastian Ott scm_attr(release);
1051d1c8f78SSebastian Ott scm_attr(res_id);
1061d1c8f78SSebastian Ott 
1071d1c8f78SSebastian Ott static struct attribute *scmdev_attrs[] = {
1081d1c8f78SSebastian Ott 	&dev_attr_persistence.attr,
1091d1c8f78SSebastian Ott 	&dev_attr_oper_state.attr,
1101d1c8f78SSebastian Ott 	&dev_attr_data_state.attr,
1111d1c8f78SSebastian Ott 	&dev_attr_rank.attr,
1121d1c8f78SSebastian Ott 	&dev_attr_release.attr,
1131d1c8f78SSebastian Ott 	&dev_attr_res_id.attr,
1141d1c8f78SSebastian Ott 	NULL,
1151d1c8f78SSebastian Ott };
1161d1c8f78SSebastian Ott 
1171d1c8f78SSebastian Ott static struct attribute_group scmdev_attr_group = {
1181d1c8f78SSebastian Ott 	.attrs = scmdev_attrs,
1191d1c8f78SSebastian Ott };
1201d1c8f78SSebastian Ott 
1211d1c8f78SSebastian Ott static const struct attribute_group *scmdev_attr_groups[] = {
1221d1c8f78SSebastian Ott 	&scmdev_attr_group,
1231d1c8f78SSebastian Ott 	NULL,
1241d1c8f78SSebastian Ott };
1251d1c8f78SSebastian Ott 
1261d1c8f78SSebastian Ott static void scmdev_release(struct device *dev)
1271d1c8f78SSebastian Ott {
1281d1c8f78SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);
1291d1c8f78SSebastian Ott 
1301d1c8f78SSebastian Ott 	kfree(scmdev);
1311d1c8f78SSebastian Ott }
1321d1c8f78SSebastian Ott 
1331d1c8f78SSebastian Ott static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
1341d1c8f78SSebastian Ott 			 unsigned int size, unsigned int max_blk_count)
1351d1c8f78SSebastian Ott {
1361d1c8f78SSebastian Ott 	dev_set_name(&scmdev->dev, "%016llx", (unsigned long long) sale->sa);
1371d1c8f78SSebastian Ott 	scmdev->nr_max_block = max_blk_count;
1381d1c8f78SSebastian Ott 	scmdev->address = sale->sa;
1391d1c8f78SSebastian Ott 	scmdev->size = 1UL << size;
1401d1c8f78SSebastian Ott 	scmdev->attrs.rank = sale->rank;
1411d1c8f78SSebastian Ott 	scmdev->attrs.persistence = sale->p;
1421d1c8f78SSebastian Ott 	scmdev->attrs.oper_state = sale->op_state;
1431d1c8f78SSebastian Ott 	scmdev->attrs.data_state = sale->data_state;
1441d1c8f78SSebastian Ott 	scmdev->attrs.rank = sale->rank;
1451d1c8f78SSebastian Ott 	scmdev->attrs.release = sale->r;
1461d1c8f78SSebastian Ott 	scmdev->attrs.res_id = sale->rid;
1471d1c8f78SSebastian Ott 	scmdev->dev.parent = scm_root;
1481d1c8f78SSebastian Ott 	scmdev->dev.bus = &scm_bus_type;
1491d1c8f78SSebastian Ott 	scmdev->dev.release = scmdev_release;
1501d1c8f78SSebastian Ott 	scmdev->dev.groups = scmdev_attr_groups;
1511d1c8f78SSebastian Ott }
1521d1c8f78SSebastian Ott 
15340ff4cc0SSebastian Ott /*
15440ff4cc0SSebastian Ott  * Check for state-changes, notify the driver and userspace.
15540ff4cc0SSebastian Ott  */
15640ff4cc0SSebastian Ott static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
15740ff4cc0SSebastian Ott {
15840ff4cc0SSebastian Ott 	struct scm_driver *scmdrv;
15940ff4cc0SSebastian Ott 	bool changed;
16040ff4cc0SSebastian Ott 
16140ff4cc0SSebastian Ott 	device_lock(&scmdev->dev);
16240ff4cc0SSebastian Ott 	changed = scmdev->attrs.rank != sale->rank ||
16340ff4cc0SSebastian Ott 		  scmdev->attrs.oper_state != sale->op_state;
16440ff4cc0SSebastian Ott 	scmdev->attrs.rank = sale->rank;
16540ff4cc0SSebastian Ott 	scmdev->attrs.oper_state = sale->op_state;
16640ff4cc0SSebastian Ott 	if (!scmdev->dev.driver)
16740ff4cc0SSebastian Ott 		goto out;
16840ff4cc0SSebastian Ott 	scmdrv = to_scm_drv(scmdev->dev.driver);
16940ff4cc0SSebastian Ott 	if (changed && scmdrv->notify)
17093481c90SSebastian Ott 		scmdrv->notify(scmdev, SCM_CHANGE);
17140ff4cc0SSebastian Ott out:
17240ff4cc0SSebastian Ott 	device_unlock(&scmdev->dev);
17340ff4cc0SSebastian Ott 	if (changed)
17440ff4cc0SSebastian Ott 		kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
17540ff4cc0SSebastian Ott }
17640ff4cc0SSebastian Ott 
17740ff4cc0SSebastian Ott static int check_address(struct device *dev, void *data)
17840ff4cc0SSebastian Ott {
17940ff4cc0SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);
18040ff4cc0SSebastian Ott 	struct sale *sale = data;
18140ff4cc0SSebastian Ott 
18240ff4cc0SSebastian Ott 	return scmdev->address == sale->sa;
18340ff4cc0SSebastian Ott }
18440ff4cc0SSebastian Ott 
18540ff4cc0SSebastian Ott static struct scm_device *scmdev_find(struct sale *sale)
18640ff4cc0SSebastian Ott {
18740ff4cc0SSebastian Ott 	struct device *dev;
18840ff4cc0SSebastian Ott 
18940ff4cc0SSebastian Ott 	dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);
19040ff4cc0SSebastian Ott 
19140ff4cc0SSebastian Ott 	return dev ? to_scm_dev(dev) : NULL;
19240ff4cc0SSebastian Ott }
19340ff4cc0SSebastian Ott 
1941d1c8f78SSebastian Ott static int scm_add(struct chsc_scm_info *scm_info, size_t num)
1951d1c8f78SSebastian Ott {
1961d1c8f78SSebastian Ott 	struct sale *sale, *scmal = scm_info->scmal;
1971d1c8f78SSebastian Ott 	struct scm_device *scmdev;
1981d1c8f78SSebastian Ott 	int ret;
1991d1c8f78SSebastian Ott 
2001d1c8f78SSebastian Ott 	for (sale = scmal; sale < scmal + num; sale++) {
20140ff4cc0SSebastian Ott 		scmdev = scmdev_find(sale);
20240ff4cc0SSebastian Ott 		if (scmdev) {
20340ff4cc0SSebastian Ott 			scmdev_update(scmdev, sale);
20440ff4cc0SSebastian Ott 			/* Release reference from scm_find(). */
20540ff4cc0SSebastian Ott 			put_device(&scmdev->dev);
20640ff4cc0SSebastian Ott 			continue;
20740ff4cc0SSebastian Ott 		}
2081d1c8f78SSebastian Ott 		scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
2091d1c8f78SSebastian Ott 		if (!scmdev)
2101d1c8f78SSebastian Ott 			return -ENODEV;
2111d1c8f78SSebastian Ott 		scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc);
2121d1c8f78SSebastian Ott 		ret = device_register(&scmdev->dev);
2131d1c8f78SSebastian Ott 		if (ret) {
2141d1c8f78SSebastian Ott 			/* Release reference from device_initialize(). */
2151d1c8f78SSebastian Ott 			put_device(&scmdev->dev);
2161d1c8f78SSebastian Ott 			return ret;
2171d1c8f78SSebastian Ott 		}
2181d1c8f78SSebastian Ott 	}
2191d1c8f78SSebastian Ott 
2201d1c8f78SSebastian Ott 	return 0;
2211d1c8f78SSebastian Ott }
2221d1c8f78SSebastian Ott 
22340ff4cc0SSebastian Ott int scm_update_information(void)
2241d1c8f78SSebastian Ott {
2251d1c8f78SSebastian Ott 	struct chsc_scm_info *scm_info;
2261d1c8f78SSebastian Ott 	u64 token = 0;
2271d1c8f78SSebastian Ott 	size_t num;
2281d1c8f78SSebastian Ott 	int ret;
2291d1c8f78SSebastian Ott 
2301d1c8f78SSebastian Ott 	scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
2311d1c8f78SSebastian Ott 	if (!scm_info)
2321d1c8f78SSebastian Ott 		return -ENOMEM;
2331d1c8f78SSebastian Ott 
2341d1c8f78SSebastian Ott 	do {
2351d1c8f78SSebastian Ott 		ret = chsc_scm_info(scm_info, token);
2361d1c8f78SSebastian Ott 		if (ret)
2371d1c8f78SSebastian Ott 			break;
2381d1c8f78SSebastian Ott 
2391d1c8f78SSebastian Ott 		num = (scm_info->response.length -
2401d1c8f78SSebastian Ott 		       (offsetof(struct chsc_scm_info, scmal) -
2411d1c8f78SSebastian Ott 			offsetof(struct chsc_scm_info, response))
2421d1c8f78SSebastian Ott 		      ) / sizeof(struct sale);
2431d1c8f78SSebastian Ott 
2441d1c8f78SSebastian Ott 		ret = scm_add(scm_info, num);
2451d1c8f78SSebastian Ott 		if (ret)
2461d1c8f78SSebastian Ott 			break;
2471d1c8f78SSebastian Ott 
2481d1c8f78SSebastian Ott 		token = scm_info->restok;
2491d1c8f78SSebastian Ott 	} while (token);
2501d1c8f78SSebastian Ott 
2511d1c8f78SSebastian Ott 	free_page((unsigned long)scm_info);
2521d1c8f78SSebastian Ott 
2531d1c8f78SSebastian Ott 	return ret;
2541d1c8f78SSebastian Ott }
2551d1c8f78SSebastian Ott 
256aebfa669SSebastian Ott static int scm_dev_avail(struct device *dev, void *unused)
257aebfa669SSebastian Ott {
258aebfa669SSebastian Ott 	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
259aebfa669SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);
260aebfa669SSebastian Ott 
261aebfa669SSebastian Ott 	if (dev->driver && scmdrv->notify)
262aebfa669SSebastian Ott 		scmdrv->notify(scmdev, SCM_AVAIL);
263aebfa669SSebastian Ott 
264aebfa669SSebastian Ott 	return 0;
265aebfa669SSebastian Ott }
266aebfa669SSebastian Ott 
267aebfa669SSebastian Ott int scm_process_availability_information(void)
268aebfa669SSebastian Ott {
269aebfa669SSebastian Ott 	return bus_for_each_dev(&scm_bus_type, NULL, NULL, scm_dev_avail);
270aebfa669SSebastian Ott }
271aebfa669SSebastian Ott 
2721d1c8f78SSebastian Ott static int __init scm_init(void)
2731d1c8f78SSebastian Ott {
2741d1c8f78SSebastian Ott 	int ret;
2751d1c8f78SSebastian Ott 
2761d1c8f78SSebastian Ott 	ret = bus_register(&scm_bus_type);
2771d1c8f78SSebastian Ott 	if (ret)
2781d1c8f78SSebastian Ott 		return ret;
2791d1c8f78SSebastian Ott 
2801d1c8f78SSebastian Ott 	scm_root = root_device_register("scm");
2811d1c8f78SSebastian Ott 	if (IS_ERR(scm_root)) {
2821d1c8f78SSebastian Ott 		bus_unregister(&scm_bus_type);
2831d1c8f78SSebastian Ott 		return PTR_ERR(scm_root);
2841d1c8f78SSebastian Ott 	}
2851d1c8f78SSebastian Ott 
2861d1c8f78SSebastian Ott 	scm_update_information();
2871d1c8f78SSebastian Ott 	return 0;
2881d1c8f78SSebastian Ott }
2891d1c8f78SSebastian Ott subsys_initcall_sync(scm_init);
290