Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

John Garry and committed by
Marc Zyngier
e15f2fa9 1c3f69b4

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