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 177418e3ea1SSuzuki K Poulose static int check_address(struct device *dev, const void *data) 17840ff4cc0SSebastian Ott { 17940ff4cc0SSebastian Ott struct scm_device *scmdev = to_scm_dev(dev); 180418e3ea1SSuzuki K Poulose const 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