aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2015-03-18 07:01:22 -0400
committerThomas Gleixner <tglx@linutronix.de>2015-04-08 17:28:28 -0400
commit1b7047edfcfb257f69e306c9afbab150cb987717 (patch)
tree68b2383acee6ce6a58900507e797e9c6178e4a5b
parentfe0c52fc003bc046380e52fe6799c96d770770cc (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.h14
-rw-r--r--include/linux/irq.h6
-rw-r--r--kernel/irq/manage.c91
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 */
367enum 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
374extern int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
375 bool *state);
376extern 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
366extern bool force_irqthreads; 380extern 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 @@
30struct seq_file; 30struct seq_file;
31struct module; 31struct module;
32struct msi_msg; 32struct msi_msg;
33enum 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 */
329struct irq_chip { 332struct 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 */
1815int 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 */
1860int 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}