xref: /openbmc/linux/drivers/s390/cio/scm.c (revision 2a81ada3)
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 
scmdev_probe(struct device * dev)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 
scmdev_remove(struct device * dev)31fc7a6209SUwe Kleine-König static void 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 
3615f83bb0SUwe Kleine-König 	if (scmdrv->remove)
3715f83bb0SUwe Kleine-König 		scmdrv->remove(scmdev);
381d1c8f78SSebastian Ott }
391d1c8f78SSebastian Ott 
scmdev_uevent(const struct device * dev,struct kobj_uevent_env * env)40*2a81ada3SGreg Kroah-Hartman static int scmdev_uevent(const struct device *dev, struct kobj_uevent_env *env)
411d1c8f78SSebastian Ott {
421d1c8f78SSebastian Ott 	return add_uevent_var(env, "MODALIAS=scm:scmdev");
431d1c8f78SSebastian Ott }
441d1c8f78SSebastian Ott 
451d1c8f78SSebastian Ott static struct bus_type scm_bus_type = {
461d1c8f78SSebastian Ott 	.name  = "scm",
471d1c8f78SSebastian Ott 	.probe = scmdev_probe,
481d1c8f78SSebastian Ott 	.remove = scmdev_remove,
491d1c8f78SSebastian Ott 	.uevent = scmdev_uevent,
501d1c8f78SSebastian Ott };
511d1c8f78SSebastian Ott 
521d1c8f78SSebastian Ott /**
531d1c8f78SSebastian Ott  * scm_driver_register() - register a scm driver
541d1c8f78SSebastian Ott  * @scmdrv: driver to be registered
551d1c8f78SSebastian Ott  */
scm_driver_register(struct scm_driver * scmdrv)561d1c8f78SSebastian Ott int scm_driver_register(struct scm_driver *scmdrv)
571d1c8f78SSebastian Ott {
581d1c8f78SSebastian Ott 	struct device_driver *drv = &scmdrv->drv;
591d1c8f78SSebastian Ott 
601d1c8f78SSebastian Ott 	drv->bus = &scm_bus_type;
611d1c8f78SSebastian Ott 
621d1c8f78SSebastian Ott 	return driver_register(drv);
631d1c8f78SSebastian Ott }
641d1c8f78SSebastian Ott EXPORT_SYMBOL_GPL(scm_driver_register);
651d1c8f78SSebastian Ott 
661d1c8f78SSebastian Ott /**
671d1c8f78SSebastian Ott  * scm_driver_unregister() - deregister a scm driver
681d1c8f78SSebastian Ott  * @scmdrv: driver to be deregistered
691d1c8f78SSebastian Ott  */
scm_driver_unregister(struct scm_driver * scmdrv)701d1c8f78SSebastian Ott void scm_driver_unregister(struct scm_driver *scmdrv)
711d1c8f78SSebastian Ott {
721d1c8f78SSebastian Ott 	driver_unregister(&scmdrv->drv);
731d1c8f78SSebastian Ott }
741d1c8f78SSebastian Ott EXPORT_SYMBOL_GPL(scm_driver_unregister);
751d1c8f78SSebastian Ott 
scm_irq_handler(struct aob * aob,blk_status_t error)762a842acaSChristoph Hellwig void scm_irq_handler(struct aob *aob, blk_status_t error)
771d1c8f78SSebastian Ott {
781d1c8f78SSebastian Ott 	struct aob_rq_header *aobrq = (void *) aob->request.data;
791d1c8f78SSebastian Ott 	struct scm_device *scmdev = aobrq->scmdev;
801d1c8f78SSebastian Ott 	struct scm_driver *scmdrv = to_scm_drv(scmdev->dev.driver);
811d1c8f78SSebastian Ott 
821d1c8f78SSebastian Ott 	scmdrv->handler(scmdev, aobrq->data, error);
831d1c8f78SSebastian Ott }
841d1c8f78SSebastian Ott EXPORT_SYMBOL_GPL(scm_irq_handler);
851d1c8f78SSebastian Ott 
861d1c8f78SSebastian Ott #define scm_attr(name)							\
871d1c8f78SSebastian Ott static ssize_t show_##name(struct device *dev,				\
881d1c8f78SSebastian Ott 	       struct device_attribute *attr, char *buf)		\
891d1c8f78SSebastian Ott {									\
901d1c8f78SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);			\
911d1c8f78SSebastian Ott 	int ret;							\
921d1c8f78SSebastian Ott 									\
93c3e6d407SSebastian Ott 	device_lock(dev);						\
941d1c8f78SSebastian Ott 	ret = sprintf(buf, "%u\n", scmdev->attrs.name);			\
95c3e6d407SSebastian Ott 	device_unlock(dev);						\
961d1c8f78SSebastian Ott 									\
971d1c8f78SSebastian Ott 	return ret;							\
981d1c8f78SSebastian Ott }									\
991d1c8f78SSebastian Ott static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
1001d1c8f78SSebastian Ott 
1011d1c8f78SSebastian Ott scm_attr(persistence);
1021d1c8f78SSebastian Ott scm_attr(oper_state);
1031d1c8f78SSebastian Ott scm_attr(data_state);
1041d1c8f78SSebastian Ott scm_attr(rank);
1051d1c8f78SSebastian Ott scm_attr(release);
1061d1c8f78SSebastian Ott scm_attr(res_id);
1071d1c8f78SSebastian Ott 
1081d1c8f78SSebastian Ott static struct attribute *scmdev_attrs[] = {
1091d1c8f78SSebastian Ott 	&dev_attr_persistence.attr,
1101d1c8f78SSebastian Ott 	&dev_attr_oper_state.attr,
1111d1c8f78SSebastian Ott 	&dev_attr_data_state.attr,
1121d1c8f78SSebastian Ott 	&dev_attr_rank.attr,
1131d1c8f78SSebastian Ott 	&dev_attr_release.attr,
1141d1c8f78SSebastian Ott 	&dev_attr_res_id.attr,
1151d1c8f78SSebastian Ott 	NULL,
1161d1c8f78SSebastian Ott };
1171d1c8f78SSebastian Ott 
1181d1c8f78SSebastian Ott static struct attribute_group scmdev_attr_group = {
1191d1c8f78SSebastian Ott 	.attrs = scmdev_attrs,
1201d1c8f78SSebastian Ott };
1211d1c8f78SSebastian Ott 
1221d1c8f78SSebastian Ott static const struct attribute_group *scmdev_attr_groups[] = {
1231d1c8f78SSebastian Ott 	&scmdev_attr_group,
1241d1c8f78SSebastian Ott 	NULL,
1251d1c8f78SSebastian Ott };
1261d1c8f78SSebastian Ott 
scmdev_release(struct device * dev)1271d1c8f78SSebastian Ott static void scmdev_release(struct device *dev)
1281d1c8f78SSebastian Ott {
1291d1c8f78SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);
1301d1c8f78SSebastian Ott 
1311d1c8f78SSebastian Ott 	kfree(scmdev);
1321d1c8f78SSebastian Ott }
1331d1c8f78SSebastian Ott 
scmdev_setup(struct scm_device * scmdev,struct sale * sale,unsigned int size,unsigned int max_blk_count)1341d1c8f78SSebastian Ott static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
1351d1c8f78SSebastian Ott 			 unsigned int size, unsigned int max_blk_count)
1361d1c8f78SSebastian Ott {
1371d1c8f78SSebastian Ott 	dev_set_name(&scmdev->dev, "%016llx", (unsigned long long) sale->sa);
1381d1c8f78SSebastian Ott 	scmdev->nr_max_block = max_blk_count;
1391d1c8f78SSebastian Ott 	scmdev->address = sale->sa;
1401d1c8f78SSebastian Ott 	scmdev->size = 1UL << size;
1411d1c8f78SSebastian Ott 	scmdev->attrs.rank = sale->rank;
1421d1c8f78SSebastian Ott 	scmdev->attrs.persistence = sale->p;
1431d1c8f78SSebastian Ott 	scmdev->attrs.oper_state = sale->op_state;
1441d1c8f78SSebastian Ott 	scmdev->attrs.data_state = sale->data_state;
1451d1c8f78SSebastian Ott 	scmdev->attrs.rank = sale->rank;
1461d1c8f78SSebastian Ott 	scmdev->attrs.release = sale->r;
1471d1c8f78SSebastian Ott 	scmdev->attrs.res_id = sale->rid;
1481d1c8f78SSebastian Ott 	scmdev->dev.parent = scm_root;
1491d1c8f78SSebastian Ott 	scmdev->dev.bus = &scm_bus_type;
1501d1c8f78SSebastian Ott 	scmdev->dev.release = scmdev_release;
1511d1c8f78SSebastian Ott 	scmdev->dev.groups = scmdev_attr_groups;
1521d1c8f78SSebastian Ott }
1531d1c8f78SSebastian Ott 
15440ff4cc0SSebastian Ott /*
15540ff4cc0SSebastian Ott  * Check for state-changes, notify the driver and userspace.
15640ff4cc0SSebastian Ott  */
scmdev_update(struct scm_device * scmdev,struct sale * sale)15740ff4cc0SSebastian Ott static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
15840ff4cc0SSebastian Ott {
15940ff4cc0SSebastian Ott 	struct scm_driver *scmdrv;
16040ff4cc0SSebastian Ott 	bool changed;
16140ff4cc0SSebastian Ott 
16240ff4cc0SSebastian Ott 	device_lock(&scmdev->dev);
16340ff4cc0SSebastian Ott 	changed = scmdev->attrs.rank != sale->rank ||
16440ff4cc0SSebastian Ott 		  scmdev->attrs.oper_state != sale->op_state;
16540ff4cc0SSebastian Ott 	scmdev->attrs.rank = sale->rank;
16640ff4cc0SSebastian Ott 	scmdev->attrs.oper_state = sale->op_state;
16740ff4cc0SSebastian Ott 	if (!scmdev->dev.driver)
16840ff4cc0SSebastian Ott 		goto out;
16940ff4cc0SSebastian Ott 	scmdrv = to_scm_drv(scmdev->dev.driver);
17040ff4cc0SSebastian Ott 	if (changed && scmdrv->notify)
17193481c90SSebastian Ott 		scmdrv->notify(scmdev, SCM_CHANGE);
17240ff4cc0SSebastian Ott out:
17340ff4cc0SSebastian Ott 	device_unlock(&scmdev->dev);
17440ff4cc0SSebastian Ott 	if (changed)
17540ff4cc0SSebastian Ott 		kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
17640ff4cc0SSebastian Ott }
17740ff4cc0SSebastian Ott 
check_address(struct device * dev,const void * data)178418e3ea1SSuzuki K Poulose static int check_address(struct device *dev, const void *data)
17940ff4cc0SSebastian Ott {
18040ff4cc0SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);
181418e3ea1SSuzuki K Poulose 	const struct sale *sale = data;
18240ff4cc0SSebastian Ott 
18340ff4cc0SSebastian Ott 	return scmdev->address == sale->sa;
18440ff4cc0SSebastian Ott }
18540ff4cc0SSebastian Ott 
scmdev_find(struct sale * sale)18640ff4cc0SSebastian Ott static struct scm_device *scmdev_find(struct sale *sale)
18740ff4cc0SSebastian Ott {
18840ff4cc0SSebastian Ott 	struct device *dev;
18940ff4cc0SSebastian Ott 
19040ff4cc0SSebastian Ott 	dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);
19140ff4cc0SSebastian Ott 
19240ff4cc0SSebastian Ott 	return dev ? to_scm_dev(dev) : NULL;
19340ff4cc0SSebastian Ott }
19440ff4cc0SSebastian Ott 
scm_add(struct chsc_scm_info * scm_info,size_t num)1951d1c8f78SSebastian Ott static int scm_add(struct chsc_scm_info *scm_info, size_t num)
1961d1c8f78SSebastian Ott {
1971d1c8f78SSebastian Ott 	struct sale *sale, *scmal = scm_info->scmal;
1981d1c8f78SSebastian Ott 	struct scm_device *scmdev;
1991d1c8f78SSebastian Ott 	int ret;
2001d1c8f78SSebastian Ott 
2011d1c8f78SSebastian Ott 	for (sale = scmal; sale < scmal + num; sale++) {
20240ff4cc0SSebastian Ott 		scmdev = scmdev_find(sale);
20340ff4cc0SSebastian Ott 		if (scmdev) {
20440ff4cc0SSebastian Ott 			scmdev_update(scmdev, sale);
20540ff4cc0SSebastian Ott 			/* Release reference from scm_find(). */
20640ff4cc0SSebastian Ott 			put_device(&scmdev->dev);
20740ff4cc0SSebastian Ott 			continue;
20840ff4cc0SSebastian Ott 		}
2091d1c8f78SSebastian Ott 		scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
2101d1c8f78SSebastian Ott 		if (!scmdev)
2111d1c8f78SSebastian Ott 			return -ENODEV;
2121d1c8f78SSebastian Ott 		scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc);
2131d1c8f78SSebastian Ott 		ret = device_register(&scmdev->dev);
2141d1c8f78SSebastian Ott 		if (ret) {
2151d1c8f78SSebastian Ott 			/* Release reference from device_initialize(). */
2161d1c8f78SSebastian Ott 			put_device(&scmdev->dev);
2171d1c8f78SSebastian Ott 			return ret;
2181d1c8f78SSebastian Ott 		}
2191d1c8f78SSebastian Ott 	}
2201d1c8f78SSebastian Ott 
2211d1c8f78SSebastian Ott 	return 0;
2221d1c8f78SSebastian Ott }
2231d1c8f78SSebastian Ott 
scm_update_information(void)22440ff4cc0SSebastian Ott int scm_update_information(void)
2251d1c8f78SSebastian Ott {
2261d1c8f78SSebastian Ott 	struct chsc_scm_info *scm_info;
2271d1c8f78SSebastian Ott 	u64 token = 0;
2281d1c8f78SSebastian Ott 	size_t num;
2291d1c8f78SSebastian Ott 	int ret;
2301d1c8f78SSebastian Ott 
2311d1c8f78SSebastian Ott 	scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
2321d1c8f78SSebastian Ott 	if (!scm_info)
2331d1c8f78SSebastian Ott 		return -ENOMEM;
2341d1c8f78SSebastian Ott 
2351d1c8f78SSebastian Ott 	do {
2361d1c8f78SSebastian Ott 		ret = chsc_scm_info(scm_info, token);
2371d1c8f78SSebastian Ott 		if (ret)
2381d1c8f78SSebastian Ott 			break;
2391d1c8f78SSebastian Ott 
2401d1c8f78SSebastian Ott 		num = (scm_info->response.length -
2411d1c8f78SSebastian Ott 		       (offsetof(struct chsc_scm_info, scmal) -
2421d1c8f78SSebastian Ott 			offsetof(struct chsc_scm_info, response))
2431d1c8f78SSebastian Ott 		      ) / sizeof(struct sale);
2441d1c8f78SSebastian Ott 
2451d1c8f78SSebastian Ott 		ret = scm_add(scm_info, num);
2461d1c8f78SSebastian Ott 		if (ret)
2471d1c8f78SSebastian Ott 			break;
2481d1c8f78SSebastian Ott 
2491d1c8f78SSebastian Ott 		token = scm_info->restok;
2501d1c8f78SSebastian Ott 	} while (token);
2511d1c8f78SSebastian Ott 
2521d1c8f78SSebastian Ott 	free_page((unsigned long)scm_info);
2531d1c8f78SSebastian Ott 
2541d1c8f78SSebastian Ott 	return ret;
2551d1c8f78SSebastian Ott }
2561d1c8f78SSebastian Ott 
scm_dev_avail(struct device * dev,void * unused)257aebfa669SSebastian Ott static int scm_dev_avail(struct device *dev, void *unused)
258aebfa669SSebastian Ott {
259aebfa669SSebastian Ott 	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
260aebfa669SSebastian Ott 	struct scm_device *scmdev = to_scm_dev(dev);
261aebfa669SSebastian Ott 
262aebfa669SSebastian Ott 	if (dev->driver && scmdrv->notify)
263aebfa669SSebastian Ott 		scmdrv->notify(scmdev, SCM_AVAIL);
264aebfa669SSebastian Ott 
265aebfa669SSebastian Ott 	return 0;
266aebfa669SSebastian Ott }
267aebfa669SSebastian Ott 
scm_process_availability_information(void)268aebfa669SSebastian Ott int scm_process_availability_information(void)
269aebfa669SSebastian Ott {
270aebfa669SSebastian Ott 	return bus_for_each_dev(&scm_bus_type, NULL, NULL, scm_dev_avail);
271aebfa669SSebastian Ott }
272aebfa669SSebastian Ott 
scm_init(void)2731d1c8f78SSebastian Ott static int __init scm_init(void)
2741d1c8f78SSebastian Ott {
2751d1c8f78SSebastian Ott 	int ret;
2761d1c8f78SSebastian Ott 
2771d1c8f78SSebastian Ott 	ret = bus_register(&scm_bus_type);
2781d1c8f78SSebastian Ott 	if (ret)
2791d1c8f78SSebastian Ott 		return ret;
2801d1c8f78SSebastian Ott 
2811d1c8f78SSebastian Ott 	scm_root = root_device_register("scm");
2821d1c8f78SSebastian Ott 	if (IS_ERR(scm_root)) {
2831d1c8f78SSebastian Ott 		bus_unregister(&scm_bus_type);
2841d1c8f78SSebastian Ott 		return PTR_ERR(scm_root);
2851d1c8f78SSebastian Ott 	}
2861d1c8f78SSebastian Ott 
2871d1c8f78SSebastian Ott 	scm_update_information();
2881d1c8f78SSebastian Ott 	return 0;
2891d1c8f78SSebastian Ott }
2901d1c8f78SSebastian Ott subsys_initcall_sync(scm_init);
291