Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/base/auxiliary_sysfs.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES
4
*/
5
6
#include <linux/auxiliary_bus.h>
7
#include <linux/slab.h>
8
9
#define AUXILIARY_MAX_IRQ_NAME 11
10
11
struct auxiliary_irq_info {
12
struct device_attribute sysfs_attr;
13
char name[AUXILIARY_MAX_IRQ_NAME];
14
};
15
16
static struct attribute *auxiliary_irq_attrs[] = {
17
NULL
18
};
19
20
static const struct attribute_group auxiliary_irqs_group = {
21
.name = "irqs",
22
.attrs = auxiliary_irq_attrs,
23
};
24
25
static int auxiliary_irq_dir_prepare(struct auxiliary_device *auxdev)
26
{
27
int ret = 0;
28
29
guard(mutex)(&auxdev->sysfs.lock);
30
if (auxdev->sysfs.irq_dir_exists)
31
return 0;
32
33
ret = devm_device_add_group(&auxdev->dev, &auxiliary_irqs_group);
34
if (ret)
35
return ret;
36
37
auxdev->sysfs.irq_dir_exists = true;
38
xa_init(&auxdev->sysfs.irqs);
39
return 0;
40
}
41
42
/**
43
* auxiliary_device_sysfs_irq_add - add a sysfs entry for the given IRQ
44
* @auxdev: auxiliary bus device to add the sysfs entry.
45
* @irq: The associated interrupt number.
46
*
47
* This function should be called after auxiliary device have successfully
48
* received the irq.
49
* The driver is responsible to add a unique irq for the auxiliary device. The
50
* driver can invoke this function from multiple thread context safely for
51
* unique irqs of the auxiliary devices. The driver must not invoke this API
52
* multiple times if the irq is already added previously.
53
*
54
* Return: zero on success or an error code on failure.
55
*/
56
int auxiliary_device_sysfs_irq_add(struct auxiliary_device *auxdev, int irq)
57
{
58
struct auxiliary_irq_info *info __free(kfree) = NULL;
59
struct device *dev = &auxdev->dev;
60
int ret;
61
62
ret = auxiliary_irq_dir_prepare(auxdev);
63
if (ret)
64
return ret;
65
66
info = kzalloc(sizeof(*info), GFP_KERNEL);
67
if (!info)
68
return -ENOMEM;
69
70
sysfs_attr_init(&info->sysfs_attr.attr);
71
snprintf(info->name, AUXILIARY_MAX_IRQ_NAME, "%d", irq);
72
73
ret = xa_insert(&auxdev->sysfs.irqs, irq, info, GFP_KERNEL);
74
if (ret)
75
return ret;
76
77
info->sysfs_attr.attr.name = info->name;
78
ret = sysfs_add_file_to_group(&dev->kobj, &info->sysfs_attr.attr,
79
auxiliary_irqs_group.name);
80
if (ret)
81
goto sysfs_add_err;
82
83
xa_store(&auxdev->sysfs.irqs, irq, no_free_ptr(info), GFP_KERNEL);
84
return 0;
85
86
sysfs_add_err:
87
xa_erase(&auxdev->sysfs.irqs, irq);
88
return ret;
89
}
90
EXPORT_SYMBOL_GPL(auxiliary_device_sysfs_irq_add);
91
92
/**
93
* auxiliary_device_sysfs_irq_remove - remove a sysfs entry for the given IRQ
94
* @auxdev: auxiliary bus device to add the sysfs entry.
95
* @irq: the IRQ to remove.
96
*
97
* This function should be called to remove an IRQ sysfs entry.
98
* The driver must invoke this API when IRQ is released by the device.
99
*/
100
void auxiliary_device_sysfs_irq_remove(struct auxiliary_device *auxdev, int irq)
101
{
102
struct auxiliary_irq_info *info __free(kfree) = xa_load(&auxdev->sysfs.irqs, irq);
103
struct device *dev = &auxdev->dev;
104
105
if (!info) {
106
dev_err(&auxdev->dev, "IRQ %d doesn't exist\n", irq);
107
return;
108
}
109
sysfs_remove_file_from_group(&dev->kobj, &info->sysfs_attr.attr,
110
auxiliary_irqs_group.name);
111
xa_erase(&auxdev->sysfs.irqs, irq);
112
}
113
EXPORT_SYMBOL_GPL(auxiliary_device_sysfs_irq_remove);
114
115