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 /kernel | |
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>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/irq/manage.c | 91 |
1 files changed, 91 insertions, 0 deletions
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 | } | ||