Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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
 11struct auxiliary_irq_info {
 12	struct device_attribute sysfs_attr;
 13	char name[AUXILIARY_MAX_IRQ_NAME];
 14};
 15
 16static struct attribute *auxiliary_irq_attrs[] = {
 17	NULL
 18};
 19
 20static const struct attribute_group auxiliary_irqs_group = {
 21	.name = "irqs",
 22	.attrs = auxiliary_irq_attrs,
 23};
 24
 25static 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 */
 56int 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
 86sysfs_add_err:
 87	xa_erase(&auxdev->sysfs.irqs, irq);
 88	return ret;
 89}
 90EXPORT_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 */
100void 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}
113EXPORT_SYMBOL_GPL(auxiliary_device_sysfs_irq_remove);