18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IOMMU sysfs class support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Red Hat, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci * Author: Alex Williamson <alex.williamson@redhat.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/iommu.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * We provide a common class "devices" group which initially has no attributes. 168c2ecf20Sopenharmony_ci * As devices are added to the IOMMU, we'll add links to the group. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_cistatic struct attribute *devices_attr[] = { 198c2ecf20Sopenharmony_ci NULL, 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const struct attribute_group devices_attr_group = { 238c2ecf20Sopenharmony_ci .name = "devices", 248c2ecf20Sopenharmony_ci .attrs = devices_attr, 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const struct attribute_group *dev_groups[] = { 288c2ecf20Sopenharmony_ci &devices_attr_group, 298c2ecf20Sopenharmony_ci NULL, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void release_device(struct device *dev) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci kfree(dev); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic struct class iommu_class = { 388c2ecf20Sopenharmony_ci .name = "iommu", 398c2ecf20Sopenharmony_ci .dev_release = release_device, 408c2ecf20Sopenharmony_ci .dev_groups = dev_groups, 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int __init iommu_dev_init(void) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return class_register(&iommu_class); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_cipostcore_initcall(iommu_dev_init); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* 508c2ecf20Sopenharmony_ci * Init the struct device for the IOMMU. IOMMU specific attributes can 518c2ecf20Sopenharmony_ci * be provided as an attribute group, allowing a unique namespace per 528c2ecf20Sopenharmony_ci * IOMMU type. 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ciint iommu_device_sysfs_add(struct iommu_device *iommu, 558c2ecf20Sopenharmony_ci struct device *parent, 568c2ecf20Sopenharmony_ci const struct attribute_group **groups, 578c2ecf20Sopenharmony_ci const char *fmt, ...) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci va_list vargs; 608c2ecf20Sopenharmony_ci int ret; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci iommu->dev = kzalloc(sizeof(*iommu->dev), GFP_KERNEL); 638c2ecf20Sopenharmony_ci if (!iommu->dev) 648c2ecf20Sopenharmony_ci return -ENOMEM; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci device_initialize(iommu->dev); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci iommu->dev->class = &iommu_class; 698c2ecf20Sopenharmony_ci iommu->dev->parent = parent; 708c2ecf20Sopenharmony_ci iommu->dev->groups = groups; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci va_start(vargs, fmt); 738c2ecf20Sopenharmony_ci ret = kobject_set_name_vargs(&iommu->dev->kobj, fmt, vargs); 748c2ecf20Sopenharmony_ci va_end(vargs); 758c2ecf20Sopenharmony_ci if (ret) 768c2ecf20Sopenharmony_ci goto error; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ret = device_add(iommu->dev); 798c2ecf20Sopenharmony_ci if (ret) 808c2ecf20Sopenharmony_ci goto error; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci dev_set_drvdata(iommu->dev, iommu); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cierror: 878c2ecf20Sopenharmony_ci put_device(iommu->dev); 888c2ecf20Sopenharmony_ci return ret; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iommu_device_sysfs_add); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_civoid iommu_device_sysfs_remove(struct iommu_device *iommu) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci dev_set_drvdata(iommu->dev, NULL); 958c2ecf20Sopenharmony_ci device_unregister(iommu->dev); 968c2ecf20Sopenharmony_ci iommu->dev = NULL; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iommu_device_sysfs_remove); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * IOMMU drivers can indicate a device is managed by a given IOMMU using 1028c2ecf20Sopenharmony_ci * this interface. A link to the device will be created in the "devices" 1038c2ecf20Sopenharmony_ci * directory of the IOMMU device in sysfs and an "iommu" link will be 1048c2ecf20Sopenharmony_ci * created under the linked device, pointing back at the IOMMU device. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ciint iommu_device_link(struct iommu_device *iommu, struct device *link) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci int ret; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!iommu || IS_ERR(iommu)) 1118c2ecf20Sopenharmony_ci return -ENODEV; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci ret = sysfs_add_link_to_group(&iommu->dev->kobj, "devices", 1148c2ecf20Sopenharmony_ci &link->kobj, dev_name(link)); 1158c2ecf20Sopenharmony_ci if (ret) 1168c2ecf20Sopenharmony_ci return ret; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = sysfs_create_link_nowarn(&link->kobj, &iommu->dev->kobj, "iommu"); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", 1218c2ecf20Sopenharmony_ci dev_name(link)); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return ret; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iommu_device_link); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_civoid iommu_device_unlink(struct iommu_device *iommu, struct device *link) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci if (!iommu || IS_ERR(iommu)) 1308c2ecf20Sopenharmony_ci return; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci sysfs_remove_link(&link->kobj, "iommu"); 1338c2ecf20Sopenharmony_ci sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", dev_name(link)); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iommu_device_unlink); 136