Skip to content

Commit e15f2fa

Browse files
John GarryMarc Zyngier
authored andcommitted
driver core: platform: Add devm_platform_get_irqs_affinity()
Drivers for multi-queue platform devices may also want managed interrupts for handling HW queue completion interrupts, so add support. The function accepts an affinity descriptor pointer, which covers all IRQs expected for the device. The function is devm class as the only current in-tree user will also use devm method for requesting the interrupts; as such, the function is made as devm as it can ensure ordering of freeing the irq and disposing of the mapping. Signed-off-by: John Garry <john.garry@huawei.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Acked-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/1606905417-183214-5-git-send-email-john.garry@huawei.com
1 parent 1c3f69b commit e15f2fa

2 files changed

Lines changed: 127 additions & 0 deletions

File tree

drivers/base/platform.c

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <linux/of_irq.h>
1616
#include <linux/module.h>
1717
#include <linux/init.h>
18+
#include <linux/interrupt.h>
19+
#include <linux/ioport.h>
1820
#include <linux/dma-mapping.h>
1921
#include <linux/memblock.h>
2022
#include <linux/err.h>
@@ -289,6 +291,125 @@ int platform_irq_count(struct platform_device *dev)
289291
}
290292
EXPORT_SYMBOL_GPL(platform_irq_count);
291293

294+
struct irq_affinity_devres {
295+
unsigned int count;
296+
unsigned int irq[];
297+
};
298+
299+
static void platform_disable_acpi_irq(struct platform_device *pdev, int index)
300+
{
301+
struct resource *r;
302+
303+
r = platform_get_resource(pdev, IORESOURCE_IRQ, index);
304+
if (r)
305+
irqresource_disabled(r, 0);
306+
}
307+
308+
static void devm_platform_get_irqs_affinity_release(struct device *dev,
309+
void *res)
310+
{
311+
struct irq_affinity_devres *ptr = res;
312+
int i;
313+
314+
for (i = 0; i < ptr->count; i++) {
315+
irq_dispose_mapping(ptr->irq[i]);
316+
317+
if (has_acpi_companion(dev))
318+
platform_disable_acpi_irq(to_platform_device(dev), i);
319+
}
320+
}
321+
322+
/**
323+
* devm_platform_get_irqs_affinity - devm method to get a set of IRQs for a
324+
* device using an interrupt affinity descriptor
325+
* @dev: platform device pointer
326+
* @affd: affinity descriptor
327+
* @minvec: minimum count of interrupt vectors
328+
* @maxvec: maximum count of interrupt vectors
329+
* @irqs: pointer holder for IRQ numbers
330+
*
331+
* Gets a set of IRQs for a platform device, and updates IRQ afffinty according
332+
* to the passed affinity descriptor
333+
*
334+
* Return: Number of vectors on success, negative error number on failure.
335+
*/
336+
int devm_platform_get_irqs_affinity(struct platform_device *dev,
337+
struct irq_affinity *affd,
338+
unsigned int minvec,
339+
unsigned int maxvec,
340+
int **irqs)
341+
{
342+
struct irq_affinity_devres *ptr;
343+
struct irq_affinity_desc *desc;
344+
size_t size;
345+
int i, ret, nvec;
346+
347+
if (!affd)
348+
return -EPERM;
349+
350+
if (maxvec < minvec)
351+
return -ERANGE;
352+
353+
nvec = platform_irq_count(dev);
354+
355+
if (nvec < minvec)
356+
return -ENOSPC;
357+
358+
nvec = irq_calc_affinity_vectors(minvec, nvec, affd);
359+
if (nvec < minvec)
360+
return -ENOSPC;
361+
362+
if (nvec > maxvec)
363+
nvec = maxvec;
364+
365+
size = sizeof(*ptr) + sizeof(unsigned int) * nvec;
366+
ptr = devres_alloc(devm_platform_get_irqs_affinity_release, size,
367+
GFP_KERNEL);
368+
if (!ptr)
369+
return -ENOMEM;
370+
371+
ptr->count = nvec;
372+
373+
for (i = 0; i < nvec; i++) {
374+
int irq = platform_get_irq(dev, i);
375+
if (irq < 0) {
376+
ret = irq;
377+
goto err_free_devres;
378+
}
379+
ptr->irq[i] = irq;
380+
}
381+
382+
desc = irq_create_affinity_masks(nvec, affd);
383+
if (!desc) {
384+
ret = -ENOMEM;
385+
goto err_free_devres;
386+
}
387+
388+
for (i = 0; i < nvec; i++) {
389+
ret = irq_update_affinity_desc(ptr->irq[i], &desc[i]);
390+
if (ret) {
391+
dev_err(&dev->dev, "failed to update irq%d affinity descriptor (%d)\n",
392+
ptr->irq[i], ret);
393+
goto err_free_desc;
394+
}
395+
}
396+
397+
devres_add(&dev->dev, ptr);
398+
399+
kfree(desc);
400+
401+
*irqs = ptr->irq;
402+
403+
return nvec;
404+
405+
err_free_desc:
406+
kfree(desc);
407+
err_free_devres:
408+
devres_free(ptr);
409+
return ret;
410+
}
411+
EXPORT_SYMBOL_GPL(devm_platform_get_irqs_affinity);
412+
292413
/**
293414
* platform_get_resource_byname - get a resource for a device by name
294415
* @dev: platform device

include/linux/platform_device.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define PLATFORM_DEVID_NONE (-1)
1616
#define PLATFORM_DEVID_AUTO (-2)
1717

18+
struct irq_affinity;
1819
struct mfd_cell;
1920
struct property_entry;
2021
struct platform_device_id;
@@ -70,6 +71,11 @@ devm_platform_ioremap_resource_byname(struct platform_device *pdev,
7071
extern int platform_get_irq(struct platform_device *, unsigned int);
7172
extern int platform_get_irq_optional(struct platform_device *, unsigned int);
7273
extern int platform_irq_count(struct platform_device *);
74+
extern int devm_platform_get_irqs_affinity(struct platform_device *dev,
75+
struct irq_affinity *affd,
76+
unsigned int minvec,
77+
unsigned int maxvec,
78+
int **irqs);
7379
extern struct resource *platform_get_resource_byname(struct platform_device *,
7480
unsigned int,
7581
const char *);

0 commit comments

Comments
 (0)