+
+out:
+ mutex_unlock(&irqs_lock);
+ return ret;
+}
+
+/**
+ * auxiliary_device_sysfs_irq_add - add a sysfs entry for the given IRQ
+ * @auxdev: auxiliary bus device to add the sysfs entry.
+ * @irq: The associated Linux interrupt number.
+ *
+ * This function should be called after auxiliary device have
successfully
+ * received the irq.
+ *
+ * Return: zero on success or an error code on failure.
+ */
+int auxiliary_device_sysfs_irq_add(struct auxiliary_device *auxdev,
int irq)
+{
+ struct device *dev = &auxdev->dev;
+ struct auxiliary_irq_info *info;
+ int ret;
+
+ ret = auxiliary_irq_create(irq);
+ if (ret)
+ return ret;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ ret = -ENOMEM;
+ goto info_err;
+ }
+
+ sysfs_attr_init(&info->sysfs_attr.attr);
+ info->sysfs_attr.attr.name = kasprintf(GFP_KERNEL, "%d", irq);
+ if (!info->sysfs_attr.attr.name) {
+ ret = -ENOMEM;
+ goto name_err;
+ }
+ info->irq = irq;
+ info->sysfs_attr.attr.mode = 0444;
+ info->sysfs_attr.show = auxiliary_irq_mode_show;
+
+ ret = xa_insert(&auxdev->irqs, irq, info, GFP_KERNEL);
+ if (ret)
+ goto auxdev_xa_err;
+
+ ret = sysfs_add_file_to_group(&dev->kobj, &info->sysfs_attr.attr,
+ auxiliary_irqs_group.name);
+ if (ret)
+ goto sysfs_add_err;
+
+ return 0;
+
+sysfs_add_err:
+ xa_erase(&auxdev->irqs, irq);
+auxdev_xa_err:
+ kfree(info->sysfs_attr.attr.name);
+name_err:
+ kfree(info);
+info_err:
+ auxiliary_irq_destroy(irq);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(auxiliary_device_sysfs_irq_add);
+
+/**
+ * auxiliary_device_sysfs_irq_remove - remove a sysfs entry for the
given IRQ
+ * @auxdev: auxiliary bus device to add the sysfs entry.
+ * @irq: the IRQ to remove.
+ *
+ * This function should be called to remove an IRQ sysfs entry.
+ */
+void auxiliary_device_sysfs_irq_remove(struct auxiliary_device
*auxdev, int irq)
+{
+ struct auxiliary_irq_info *info = xa_load(&auxdev->irqs, irq);
+ struct device *dev = &auxdev->dev;
+
+ sysfs_remove_file_from_group(&dev->kobj, &info->sysfs_attr.attr,
+ auxiliary_irqs_group.name);
+ xa_erase(&auxdev->irqs, irq);
+ kfree(info->sysfs_attr.attr.name);
+ kfree(info);
+ auxiliary_irq_destroy(irq);
+}
+EXPORT_SYMBOL_GPL(auxiliary_device_sysfs_irq_remove);
+#endif
+
static const struct auxiliary_device_id *auxiliary_match_id(const
struct auxiliary_device_id *id,
const struct
auxiliary_device *auxdev)
{
@@ -295,6 +452,7 @@ EXPORT_SYMBOL_GPL(auxiliary_device_init);
* __auxiliary_device_add - add an auxiliary bus device
* @auxdev: auxiliary bus device to add to the bus
* @modname: name of the parent device's driver module
+ * @irqs_sysfs_enable: whether to enable IRQs sysfs
*
* This is the third step in the three-step process to register an
* auxiliary_device.
@@ -310,7 +468,8 @@ EXPORT_SYMBOL_GPL(auxiliary_device_init);
* parameter. Only if a user requires a custom name would this
version be
* called directly.
*/
-int __auxiliary_device_add(struct auxiliary_device *auxdev, const
char *modname)
+int __auxiliary_device_add(struct auxiliary_device *auxdev, const
char *modname,
+ bool irqs_sysfs_enable)
{
struct device *dev = &auxdev->dev;
int ret;
@@ -325,6 +484,10 @@ int __auxiliary_device_add(struct
auxiliary_device *auxdev, const char *modname)
dev_err(dev, "auxiliary device dev_set_name failed:
%d\n", ret);
return ret;
}
+ if (irqs_sysfs_enable) {
+ dev->groups = auxiliary_irqs_groups;
+ xa_init(&auxdev->irqs);
+ }
ret = device_add(dev);
if (ret)
diff --git a/include/linux/auxiliary_bus.h
b/include/linux/auxiliary_bus.h
index de21d9d24a95..760fadb26620 100644
--- a/include/linux/auxiliary_bus.h
+++ b/include/linux/auxiliary_bus.h
@@ -58,6 +58,7 @@
* in
* @name: Match name found by the auxiliary device driver,
* @id: unique identitier if multiple devices of the same name are
exported,
+ * @irqs: irqs xarray contains irq indices which are used by the device,
*
* An auxiliary_device represents a part of its parent device's
functionality.
* It is given a name that, combined with the registering drivers
@@ -138,6 +139,7 @@
struct auxiliary_device {
struct device dev;
const char *name;
+ struct xarray irqs;
u32 id;
};
@@ -209,8 +211,26 @@ static inline struct auxiliary_driver
*to_auxiliary_drv(struct device_driver *dr
}
int auxiliary_device_init(struct auxiliary_device *auxdev);
-int __auxiliary_device_add(struct auxiliary_device *auxdev, const
char *modname);
-#define auxiliary_device_add(auxdev) __auxiliary_device_add(auxdev,
KBUILD_MODNAME)
+int __auxiliary_device_add(struct auxiliary_device *auxdev, const
char *modname,
+ bool irqs_sysfs_enable);
+#define auxiliary_device_add(auxdev) __auxiliary_device_add(auxdev,
KBUILD_MODNAME, false)
+#define auxiliary_device_add_with_irqs(auxdev) \
+ __auxiliary_device_add(auxdev, KBUILD_MODNAME, true)
+
+#ifdef CONFIG_SYSFS
+int auxiliary_device_sysfs_irq_add(struct auxiliary_device *auxdev,
int irq);
+void auxiliary_device_sysfs_irq_remove(struct auxiliary_device *auxdev,
+ int irq);
+#else /* CONFIG_SYSFS */
+static inline int
+auxiliary_device_sysfs_irq_add(struct auxiliary_device *auxdev, int irq)
+{
+ return 0;
+}
+
+static inline void
+auxiliary_device_sysfs_irq_remove(struct auxiliary_device *auxdev,
int irq) {}
+#endif
static inline void auxiliary_device_uninit(struct auxiliary_device
*auxdev)
{