162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IOMMU sysfs class support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014 Red Hat, Inc.  All rights reserved.
662306a36Sopenharmony_ci *     Author: Alex Williamson <alex.williamson@redhat.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/iommu.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * We provide a common class "devices" group which initially has no attributes.
1662306a36Sopenharmony_ci * As devices are added to the IOMMU, we'll add links to the group.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_cistatic struct attribute *devices_attr[] = {
1962306a36Sopenharmony_ci	NULL,
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic const struct attribute_group devices_attr_group = {
2362306a36Sopenharmony_ci	.name = "devices",
2462306a36Sopenharmony_ci	.attrs = devices_attr,
2562306a36Sopenharmony_ci};
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic const struct attribute_group *dev_groups[] = {
2862306a36Sopenharmony_ci	&devices_attr_group,
2962306a36Sopenharmony_ci	NULL,
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic void release_device(struct device *dev)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	kfree(dev);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic struct class iommu_class = {
3862306a36Sopenharmony_ci	.name = "iommu",
3962306a36Sopenharmony_ci	.dev_release = release_device,
4062306a36Sopenharmony_ci	.dev_groups = dev_groups,
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int __init iommu_dev_init(void)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return class_register(&iommu_class);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_cipostcore_initcall(iommu_dev_init);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci * Init the struct device for the IOMMU. IOMMU specific attributes can
5162306a36Sopenharmony_ci * be provided as an attribute group, allowing a unique namespace per
5262306a36Sopenharmony_ci * IOMMU type.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_ciint iommu_device_sysfs_add(struct iommu_device *iommu,
5562306a36Sopenharmony_ci			   struct device *parent,
5662306a36Sopenharmony_ci			   const struct attribute_group **groups,
5762306a36Sopenharmony_ci			   const char *fmt, ...)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	va_list vargs;
6062306a36Sopenharmony_ci	int ret;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	iommu->dev = kzalloc(sizeof(*iommu->dev), GFP_KERNEL);
6362306a36Sopenharmony_ci	if (!iommu->dev)
6462306a36Sopenharmony_ci		return -ENOMEM;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	device_initialize(iommu->dev);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	iommu->dev->class = &iommu_class;
6962306a36Sopenharmony_ci	iommu->dev->parent = parent;
7062306a36Sopenharmony_ci	iommu->dev->groups = groups;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	va_start(vargs, fmt);
7362306a36Sopenharmony_ci	ret = kobject_set_name_vargs(&iommu->dev->kobj, fmt, vargs);
7462306a36Sopenharmony_ci	va_end(vargs);
7562306a36Sopenharmony_ci	if (ret)
7662306a36Sopenharmony_ci		goto error;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ret = device_add(iommu->dev);
7962306a36Sopenharmony_ci	if (ret)
8062306a36Sopenharmony_ci		goto error;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	dev_set_drvdata(iommu->dev, iommu);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return 0;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cierror:
8762306a36Sopenharmony_ci	put_device(iommu->dev);
8862306a36Sopenharmony_ci	return ret;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iommu_device_sysfs_add);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_civoid iommu_device_sysfs_remove(struct iommu_device *iommu)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	dev_set_drvdata(iommu->dev, NULL);
9562306a36Sopenharmony_ci	device_unregister(iommu->dev);
9662306a36Sopenharmony_ci	iommu->dev = NULL;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iommu_device_sysfs_remove);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * IOMMU drivers can indicate a device is managed by a given IOMMU using
10262306a36Sopenharmony_ci * this interface.  A link to the device will be created in the "devices"
10362306a36Sopenharmony_ci * directory of the IOMMU device in sysfs and an "iommu" link will be
10462306a36Sopenharmony_ci * created under the linked device, pointing back at the IOMMU device.
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_ciint iommu_device_link(struct iommu_device *iommu, struct device *link)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	int ret;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	ret = sysfs_add_link_to_group(&iommu->dev->kobj, "devices",
11162306a36Sopenharmony_ci				      &link->kobj, dev_name(link));
11262306a36Sopenharmony_ci	if (ret)
11362306a36Sopenharmony_ci		return ret;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	ret = sysfs_create_link_nowarn(&link->kobj, &iommu->dev->kobj, "iommu");
11662306a36Sopenharmony_ci	if (ret)
11762306a36Sopenharmony_ci		sysfs_remove_link_from_group(&iommu->dev->kobj, "devices",
11862306a36Sopenharmony_ci					     dev_name(link));
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return ret;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_civoid iommu_device_unlink(struct iommu_device *iommu, struct device *link)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	sysfs_remove_link(&link->kobj, "iommu");
12662306a36Sopenharmony_ci	sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", dev_name(link));
12762306a36Sopenharmony_ci}
128