| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * IOMMU sysfs class support | 
 |  * | 
 |  * Copyright (C) 2014 Red Hat, Inc.  All rights reserved. | 
 |  *     Author: Alex Williamson <alex.williamson@redhat.com> | 
 |  */ | 
 |  | 
 | #include <linux/device.h> | 
 | #include <linux/iommu.h> | 
 | #include <linux/init.h> | 
 | #include <linux/slab.h> | 
 |  | 
 | /* | 
 |  * We provide a common class "devices" group which initially has no attributes. | 
 |  * As devices are added to the IOMMU, we'll add links to the group. | 
 |  */ | 
 | static struct attribute *devices_attr[] = { | 
 | 	NULL, | 
 | }; | 
 |  | 
 | static const struct attribute_group devices_attr_group = { | 
 | 	.name = "devices", | 
 | 	.attrs = devices_attr, | 
 | }; | 
 |  | 
 | static const struct attribute_group *dev_groups[] = { | 
 | 	&devices_attr_group, | 
 | 	NULL, | 
 | }; | 
 |  | 
 | static void release_device(struct device *dev) | 
 | { | 
 | 	kfree(dev); | 
 | } | 
 |  | 
 | static struct class iommu_class = { | 
 | 	.name = "iommu", | 
 | 	.dev_release = release_device, | 
 | 	.dev_groups = dev_groups, | 
 | }; | 
 |  | 
 | static int __init iommu_dev_init(void) | 
 | { | 
 | 	return class_register(&iommu_class); | 
 | } | 
 | postcore_initcall(iommu_dev_init); | 
 |  | 
 | /* | 
 |  * Init the struct device for the IOMMU. IOMMU specific attributes can | 
 |  * be provided as an attribute group, allowing a unique namespace per | 
 |  * IOMMU type. | 
 |  */ | 
 | int iommu_device_sysfs_add(struct iommu_device *iommu, | 
 | 			   struct device *parent, | 
 | 			   const struct attribute_group **groups, | 
 | 			   const char *fmt, ...) | 
 | { | 
 | 	va_list vargs; | 
 | 	int ret; | 
 |  | 
 | 	iommu->dev = kzalloc(sizeof(*iommu->dev), GFP_KERNEL); | 
 | 	if (!iommu->dev) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	device_initialize(iommu->dev); | 
 |  | 
 | 	iommu->dev->class = &iommu_class; | 
 | 	iommu->dev->parent = parent; | 
 | 	iommu->dev->groups = groups; | 
 |  | 
 | 	va_start(vargs, fmt); | 
 | 	ret = kobject_set_name_vargs(&iommu->dev->kobj, fmt, vargs); | 
 | 	va_end(vargs); | 
 | 	if (ret) | 
 | 		goto error; | 
 |  | 
 | 	ret = device_add(iommu->dev); | 
 | 	if (ret) | 
 | 		goto error; | 
 |  | 
 | 	dev_set_drvdata(iommu->dev, iommu); | 
 |  | 
 | 	return 0; | 
 |  | 
 | error: | 
 | 	put_device(iommu->dev); | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL_GPL(iommu_device_sysfs_add); | 
 |  | 
 | void iommu_device_sysfs_remove(struct iommu_device *iommu) | 
 | { | 
 | 	dev_set_drvdata(iommu->dev, NULL); | 
 | 	device_unregister(iommu->dev); | 
 | 	iommu->dev = NULL; | 
 | } | 
 | EXPORT_SYMBOL_GPL(iommu_device_sysfs_remove); | 
 |  | 
 | /* | 
 |  * IOMMU drivers can indicate a device is managed by a given IOMMU using | 
 |  * this interface.  A link to the device will be created in the "devices" | 
 |  * directory of the IOMMU device in sysfs and an "iommu" link will be | 
 |  * created under the linked device, pointing back at the IOMMU device. | 
 |  */ | 
 | int iommu_device_link(struct iommu_device *iommu, struct device *link) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	if (!iommu || IS_ERR(iommu)) | 
 | 		return -ENODEV; | 
 |  | 
 | 	ret = sysfs_add_link_to_group(&iommu->dev->kobj, "devices", | 
 | 				      &link->kobj, dev_name(link)); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	ret = sysfs_create_link_nowarn(&link->kobj, &iommu->dev->kobj, "iommu"); | 
 | 	if (ret) | 
 | 		sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", | 
 | 					     dev_name(link)); | 
 |  | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL_GPL(iommu_device_link); | 
 |  | 
 | void iommu_device_unlink(struct iommu_device *iommu, struct device *link) | 
 | { | 
 | 	if (!iommu || IS_ERR(iommu)) | 
 | 		return; | 
 |  | 
 | 	sysfs_remove_link(&link->kobj, "iommu"); | 
 | 	sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", dev_name(link)); | 
 | } | 
 | EXPORT_SYMBOL_GPL(iommu_device_unlink); |