diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2015-03-18 07:01:22 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-04-08 17:28:28 -0400 |
commit | 1b7047edfcfb257f69e306c9afbab150cb987717 (patch) | |
tree | 68b2383acee6ce6a58900507e797e9c6178e4a5b | |
parent | fe0c52fc003bc046380e52fe6799c96d770770cc (diff) |
genirq: Allow the irqchip state of an IRQ to be save/restored
There is a number of cases where a kernel subsystem may want to
introspect the state of an interrupt at the irqchip level:
- When a peripheral is shared between virtual machines,
its interrupt state becomes part of the guest's state,
and must be switched accordingly. KVM on arm/arm64 requires
this for its guest-visible timer
- Some GPIO controllers seem to require peeking into the
interrupt controller they are connected to to report
their internal state
This seem to be a pattern that is common enough for the core code
to try and support this without too many horrible hacks. Introduce
a pair of accessors (irq_get_irqchip_state/irq_set_irqchip_state)
to retrieve the bits that can be of interest to another subsystem:
pending, active, and masked.
- irq_get_irqchip_state returns the state of the interrupt according
to a parameter set to IRQCHIP_STATE_PENDING, IRQCHIP_STATE_ACTIVE,
IRQCHIP_STATE_MASKED or IRQCHIP_STATE_LINE_LEVEL.
- irq_set_irqchip_state similarly sets the state of the interrupt.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Tested-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: Abhijeet Dharmapurikar <adharmap@codeaurora.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Phong Vo <pvo@apm.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Tin Huynh <tnhuynh@apm.com>
Cc: Y Vo <yvo@apm.com>
Cc: Toan Le <toanle@apm.com>
Cc: Bjorn Andersson <bjorn@kryo.se>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Arnd Bergmann <arnd@arndb.de>
Link: http://lkml.kernel.org/r/1426676484-21812-2-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | include/linux/interrupt.h | 14 | ||||
-rw-r--r-- | include/linux/irq.h | 6 | ||||
-rw-r--r-- | kernel/irq/manage.c | 91 |
3 files changed, 111 insertions, 0 deletions
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 150dde04cf4f..950ae4501826 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
@@ -361,6 +361,20 @@ static inline int disable_irq_wake(unsigned int irq) | |||
361 | return irq_set_irq_wake(irq, 0); | 361 | return irq_set_irq_wake(irq, 0); |
362 | } | 362 | } |
363 | 363 | ||
364 | /* | ||
365 | * irq_get_irqchip_state/irq_set_irqchip_state specific flags | ||
366 | */ | ||
367 | enum irqchip_irq_state { | ||
368 | IRQCHIP_STATE_PENDING, /* Is interrupt pending? */ | ||
369 | IRQCHIP_STATE_ACTIVE, /* Is interrupt in progress? */ | ||
370 | IRQCHIP_STATE_MASKED, /* Is interrupt masked? */ | ||
371 | IRQCHIP_STATE_LINE_LEVEL, /* Is IRQ line high? */ | ||
372 | }; | ||
373 | |||
374 | extern int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, | ||
375 | bool *state); | ||
376 | extern int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, | ||
377 | bool state); | ||
364 | 378 | ||
365 | #ifdef CONFIG_IRQ_FORCED_THREADING | 379 | #ifdef CONFIG_IRQ_FORCED_THREADING |
366 | extern bool force_irqthreads; | 380 | extern bool force_irqthreads; |
diff --git a/include/linux/irq.h b/include/linux/irq.h index d09ec7a1243e..77dd2e7f93f4 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h | |||
@@ -30,6 +30,7 @@ | |||
30 | struct seq_file; | 30 | struct seq_file; |
31 | struct module; | 31 | struct module; |
32 | struct msi_msg; | 32 | struct msi_msg; |
33 | enum irqchip_irq_state; | ||
33 | 34 | ||
34 | /* | 35 | /* |
35 | * IRQ line status. | 36 | * IRQ line status. |
@@ -324,6 +325,8 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) | |||
324 | * irq_request_resources | 325 | * irq_request_resources |
325 | * @irq_compose_msi_msg: optional to compose message content for MSI | 326 | * @irq_compose_msi_msg: optional to compose message content for MSI |
326 | * @irq_write_msi_msg: optional to write message content for MSI | 327 | * @irq_write_msi_msg: optional to write message content for MSI |
328 | * @irq_get_irqchip_state: return the internal state of an interrupt | ||
329 | * @irq_set_irqchip_state: set the internal state of a interrupt | ||
327 | * @flags: chip specific flags | 330 | * @flags: chip specific flags |
328 | */ | 331 | */ |
329 | struct irq_chip { | 332 | struct irq_chip { |
@@ -363,6 +366,9 @@ struct irq_chip { | |||
363 | void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg); | 366 | void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg); |
364 | void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg); | 367 | void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg); |
365 | 368 | ||
369 | int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state); | ||
370 | int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state); | ||
371 | |||
366 | unsigned long flags; | 372 | unsigned long flags; |
367 | }; | 373 | }; |
368 | 374 | ||
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index c0a1100d911f..e68932bb308e 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -1798,3 +1798,94 @@ int request_percpu_irq(unsigned int irq, irq_handler_t handler, | |||
1798 | 1798 | ||
1799 | return retval; | 1799 | return retval; |
1800 | } | 1800 | } |
1801 | |||
1802 | /** | ||
1803 | * irq_get_irqchip_state - returns the irqchip state of a interrupt. | ||
1804 | * @irq: Interrupt line that is forwarded to a VM | ||
1805 | * @which: One of IRQCHIP_STATE_* the caller wants to know about | ||
1806 | * @state: a pointer to a boolean where the state is to be storeed | ||
1807 | * | ||
1808 | * This call snapshots the internal irqchip state of an | ||
1809 | * interrupt, returning into @state the bit corresponding to | ||
1810 | * stage @which | ||
1811 | * | ||
1812 | * This function should be called with preemption disabled if the | ||
1813 | * interrupt controller has per-cpu registers. | ||
1814 | */ | ||
1815 | int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which, | ||
1816 | bool *state) | ||
1817 | { | ||
1818 | struct irq_desc *desc; | ||
1819 | struct irq_data *data; | ||
1820 | struct irq_chip *chip; | ||
1821 | unsigned long flags; | ||
1822 | int err = -EINVAL; | ||
1823 | |||
1824 | desc = irq_get_desc_buslock(irq, &flags, 0); | ||
1825 | if (!desc) | ||
1826 | return err; | ||
1827 | |||
1828 | data = irq_desc_get_irq_data(desc); | ||
1829 | |||
1830 | do { | ||
1831 | chip = irq_data_get_irq_chip(data); | ||
1832 | if (chip->irq_get_irqchip_state) | ||
1833 | break; | ||
1834 | #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY | ||
1835 | data = data->parent_data; | ||
1836 | #else | ||
1837 | data = NULL; | ||
1838 | #endif | ||
1839 | } while (data); | ||
1840 | |||
1841 | if (data) | ||
1842 | err = chip->irq_get_irqchip_state(data, which, state); | ||
1843 | |||
1844 | irq_put_desc_busunlock(desc, flags); | ||
1845 | return err; | ||
1846 | } | ||
1847 | |||
1848 | /** | ||
1849 | * irq_set_irqchip_state - set the state of a forwarded interrupt. | ||
1850 | * @irq: Interrupt line that is forwarded to a VM | ||
1851 | * @which: State to be restored (one of IRQCHIP_STATE_*) | ||
1852 | * @val: Value corresponding to @which | ||
1853 | * | ||
1854 | * This call sets the internal irqchip state of an interrupt, | ||
1855 | * depending on the value of @which. | ||
1856 | * | ||
1857 | * This function should be called with preemption disabled if the | ||
1858 | * interrupt controller has per-cpu registers. | ||
1859 | */ | ||
1860 | int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which, | ||
1861 | bool val) | ||
1862 | { | ||
1863 | struct irq_desc *desc; | ||
1864 | struct irq_data *data; | ||
1865 | struct irq_chip *chip; | ||
1866 | unsigned long flags; | ||
1867 | int err = -EINVAL; | ||
1868 | |||
1869 | desc = irq_get_desc_buslock(irq, &flags, 0); | ||
1870 | if (!desc) | ||
1871 | return err; | ||
1872 | |||
1873 | data = irq_desc_get_irq_data(desc); | ||
1874 | |||
1875 | do { | ||
1876 | chip = irq_data_get_irq_chip(data); | ||
1877 | if (chip->irq_set_irqchip_state) | ||
1878 | break; | ||
1879 | #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY | ||
1880 | data = data->parent_data; | ||
1881 | #else | ||
1882 | data = NULL; | ||
1883 | #endif | ||
1884 | } while (data); | ||
1885 | |||
1886 | if (data) | ||
1887 | err = chip->irq_set_irqchip_state(data, which, val); | ||
1888 | |||
1889 | irq_put_desc_busunlock(desc, flags); | ||
1890 | return err; | ||
1891 | } | ||