Skip to content

Commit be45beb

Browse files
jonhunterMarc Zyngier
authored andcommitted
genirq: Add runtime power management support for IRQ chips
Some IRQ chips may be located in a power domain outside of the CPU subsystem and hence will require device specific runtime power management. In order to support such IRQ chips, add a pointer for a device structure to the irq_chip structure, and if this pointer is populated by the IRQ chip driver and CONFIG_PM is selected in the kernel configuration, then the pm_runtime_get/put APIs for this chip will be called when an IRQ is requested/freed, respectively. Reviewed-by: Kevin Hilman <khilman@baylibre.com> Signed-off-by: Jon Hunter <jonathanh@nvidia.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
1 parent 1e2a7d7 commit be45beb

File tree

4 files changed

+75
-1
lines changed

4 files changed

+75
-1
lines changed

include/linux/irq.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
315315
/**
316316
* struct irq_chip - hardware interrupt chip descriptor
317317
*
318+
* @parent_device: pointer to parent device for irqchip
318319
* @name: name for /proc/interrupts
319320
* @irq_startup: start up the interrupt (defaults to ->enable if NULL)
320321
* @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)
@@ -354,6 +355,7 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
354355
* @flags: chip specific flags
355356
*/
356357
struct irq_chip {
358+
struct device *parent_device;
357359
const char *name;
358360
unsigned int (*irq_startup)(struct irq_data *data);
359361
void (*irq_shutdown)(struct irq_data *data);
@@ -488,6 +490,8 @@ extern void handle_bad_irq(struct irq_desc *desc);
488490
extern void handle_nested_irq(unsigned int irq);
489491

490492
extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
493+
extern int irq_chip_pm_get(struct irq_data *data);
494+
extern int irq_chip_pm_put(struct irq_data *data);
491495
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
492496
extern void irq_chip_enable_parent(struct irq_data *data);
493497
extern void irq_chip_disable_parent(struct irq_data *data);

kernel/irq/chip.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,3 +1093,43 @@ int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
10931093

10941094
return 0;
10951095
}
1096+
1097+
/**
1098+
* irq_chip_pm_get - Enable power for an IRQ chip
1099+
* @data: Pointer to interrupt specific data
1100+
*
1101+
* Enable the power to the IRQ chip referenced by the interrupt data
1102+
* structure.
1103+
*/
1104+
int irq_chip_pm_get(struct irq_data *data)
1105+
{
1106+
int retval;
1107+
1108+
if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device) {
1109+
retval = pm_runtime_get_sync(data->chip->parent_device);
1110+
if (retval < 0) {
1111+
pm_runtime_put_noidle(data->chip->parent_device);
1112+
return retval;
1113+
}
1114+
}
1115+
1116+
return 0;
1117+
}
1118+
1119+
/**
1120+
* irq_chip_pm_put - Disable power for an IRQ chip
1121+
* @data: Pointer to interrupt specific data
1122+
*
1123+
* Disable the power to the IRQ chip referenced by the interrupt data
1124+
* structure, belongs. Note that power will only be disabled, once this
1125+
* function has been called for all IRQs that have called irq_chip_pm_get().
1126+
*/
1127+
int irq_chip_pm_put(struct irq_data *data)
1128+
{
1129+
int retval = 0;
1130+
1131+
if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device)
1132+
retval = pm_runtime_put(data->chip->parent_device);
1133+
1134+
return (retval < 0) ? retval : 0;
1135+
}

kernel/irq/internals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88
#include <linux/irqdesc.h>
99
#include <linux/kernel_stat.h>
10+
#include <linux/pm_runtime.h>
1011

1112
#ifdef CONFIG_SPARSE_IRQ
1213
# define IRQ_BITMAP_BITS (NR_IRQS + 8196)

kernel/irq/manage.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1416,10 +1416,18 @@ int setup_irq(unsigned int irq, struct irqaction *act)
14161416

14171417
if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
14181418
return -EINVAL;
1419+
1420+
retval = irq_chip_pm_get(&desc->irq_data);
1421+
if (retval < 0)
1422+
return retval;
1423+
14191424
chip_bus_lock(desc);
14201425
retval = __setup_irq(irq, desc, act);
14211426
chip_bus_sync_unlock(desc);
14221427

1428+
if (retval)
1429+
irq_chip_pm_put(&desc->irq_data);
1430+
14231431
return retval;
14241432
}
14251433
EXPORT_SYMBOL_GPL(setup_irq);
@@ -1513,6 +1521,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
15131521
}
15141522
}
15151523

1524+
irq_chip_pm_put(&desc->irq_data);
15161525
module_put(desc->owner);
15171526
kfree(action->secondary);
15181527
return action;
@@ -1655,11 +1664,16 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
16551664
action->name = devname;
16561665
action->dev_id = dev_id;
16571666

1667+
retval = irq_chip_pm_get(&desc->irq_data);
1668+
if (retval < 0)
1669+
return retval;
1670+
16581671
chip_bus_lock(desc);
16591672
retval = __setup_irq(irq, desc, action);
16601673
chip_bus_sync_unlock(desc);
16611674

16621675
if (retval) {
1676+
irq_chip_pm_put(&desc->irq_data);
16631677
kfree(action->secondary);
16641678
kfree(action);
16651679
}
@@ -1836,6 +1850,7 @@ static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_
18361850

18371851
unregister_handler_proc(irq, action);
18381852

1853+
irq_chip_pm_put(&desc->irq_data);
18391854
module_put(desc->owner);
18401855
return action;
18411856

@@ -1898,10 +1913,18 @@ int setup_percpu_irq(unsigned int irq, struct irqaction *act)
18981913

18991914
if (!desc || !irq_settings_is_per_cpu_devid(desc))
19001915
return -EINVAL;
1916+
1917+
retval = irq_chip_pm_get(&desc->irq_data);
1918+
if (retval < 0)
1919+
return retval;
1920+
19011921
chip_bus_lock(desc);
19021922
retval = __setup_irq(irq, desc, act);
19031923
chip_bus_sync_unlock(desc);
19041924

1925+
if (retval)
1926+
irq_chip_pm_put(&desc->irq_data);
1927+
19051928
return retval;
19061929
}
19071930

@@ -1945,12 +1968,18 @@ int request_percpu_irq(unsigned int irq, irq_handler_t handler,
19451968
action->name = devname;
19461969
action->percpu_dev_id = dev_id;
19471970

1971+
retval = irq_chip_pm_get(&desc->irq_data);
1972+
if (retval < 0)
1973+
return retval;
1974+
19481975
chip_bus_lock(desc);
19491976
retval = __setup_irq(irq, desc, action);
19501977
chip_bus_sync_unlock(desc);
19511978

1952-
if (retval)
1979+
if (retval) {
1980+
irq_chip_pm_put(&desc->irq_data);
19531981
kfree(action);
1982+
}
19541983

19551984
return retval;
19561985
}

0 commit comments

Comments
 (0)