* [PATCH v3 1/3] genirq: Allow the irqchip state of an IRQ to be save/restored
2015-01-07 17:50 [PATCH v3 0/3] genirq: Saving/restoring the irqchip state of an irq line Marc Zyngier
@ 2015-01-07 17:51 ` Marc Zyngier
2015-02-05 23:33 ` Bjorn Andersson
2015-01-07 17:51 ` [PATCH v3 2/3] irqchip: GIC: Add support for irq_{get, set}_irqchip_state Marc Zyngier
2015-01-07 17:51 ` [PATCH v3 3/3] irqchip: GICv3: " Marc Zyngier
2 siblings, 1 reply; 6+ messages in thread
From: Marc Zyngier @ 2015-01-07 17:51 UTC (permalink / raw)
To: linux-arm-kernel
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.
Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Tested-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
include/linux/interrupt.h | 14 ++++++++
include/linux/irq.h | 6 ++++
kernel/irq/manage.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 111 insertions(+)
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index d9b05b5..fa7040e 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -356,6 +356,20 @@ static inline int disable_irq_wake(unsigned int irq)
return irq_set_irq_wake(irq, 0);
}
+/*
+ * irq_get_irqchip_state/irq_set_irqchip_state specific flags
+ */
+enum irqchip_irq_state {
+ IRQCHIP_STATE_PENDING, /* Is interrupt pending? */
+ IRQCHIP_STATE_ACTIVE, /* Is interrupt in progress? */
+ IRQCHIP_STATE_MASKED, /* Is interrupt masked? */
+ IRQCHIP_STATE_LINE_LEVEL, /* Is IRQ line high? */
+};
+
+extern int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
+ bool *state);
+extern int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
+ bool state);
#ifdef CONFIG_IRQ_FORCED_THREADING
extern bool force_irqthreads;
diff --git a/include/linux/irq.h b/include/linux/irq.h
index d09ec7a..77dd2e7 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -30,6 +30,7 @@
struct seq_file;
struct module;
struct msi_msg;
+enum irqchip_irq_state;
/*
* IRQ line status.
@@ -324,6 +325,8 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
* irq_request_resources
* @irq_compose_msi_msg: optional to compose message content for MSI
* @irq_write_msi_msg: optional to write message content for MSI
+ * @irq_get_irqchip_state: return the internal state of an interrupt
+ * @irq_set_irqchip_state: set the internal state of a interrupt
* @flags: chip specific flags
*/
struct irq_chip {
@@ -363,6 +366,9 @@ struct irq_chip {
void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);
+ int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
+ int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);
+
unsigned long flags;
};
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 8069237..acb401f5 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -1758,3 +1758,94 @@ int request_percpu_irq(unsigned int irq, irq_handler_t handler,
return retval;
}
+
+/**
+ * irq_get_irqchip_state - returns the irqchip state of a interrupt.
+ * @irq: Interrupt line that is forwarded to a VM
+ * @which: One of IRQCHIP_STATE_* the caller wants to know about
+ * @state: a pointer to a boolean where the state is to be storeed
+ *
+ * This call snapshots the internal irqchip state of an
+ * interrupt, returning into @state the bit corresponding to
+ * stage @which
+ *
+ * This function should be called with preemption disabled if the
+ * interrupt controller has per-cpu registers.
+ */
+int irq_get_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
+ bool *state)
+{
+ struct irq_desc *desc;
+ struct irq_data *data;
+ struct irq_chip *chip;
+ unsigned long flags;
+ int err = -EINVAL;
+
+ desc = irq_get_desc_buslock(irq, &flags, 0);
+ if (!desc)
+ return err;
+
+ data = irq_desc_get_irq_data(desc);
+
+ do {
+ chip = irq_data_get_irq_chip(data);
+ if (chip->irq_get_irqchip_state)
+ break;
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ data = data->parent_data;
+#else
+ data = NULL;
+#endif
+ } while (data);
+
+ if (data)
+ err = chip->irq_get_irqchip_state(data, which, state);
+
+ irq_put_desc_busunlock(desc, flags);
+ return err;
+}
+
+/**
+ * irq_set_irqchip_state - set the state of a forwarded interrupt.
+ * @irq: Interrupt line that is forwarded to a VM
+ * @which: State to be restored (one of IRQCHIP_STATE_*)
+ * @val: Value corresponding to @which
+ *
+ * This call sets the internal irqchip state of an interrupt,
+ * depending on the value of @which.
+ *
+ * This function should be called with preemption disabled if the
+ * interrupt controller has per-cpu registers.
+ */
+int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
+ bool val)
+{
+ struct irq_desc *desc;
+ struct irq_data *data;
+ struct irq_chip *chip;
+ unsigned long flags;
+ int err = -EINVAL;
+
+ desc = irq_get_desc_buslock(irq, &flags, 0);
+ if (!desc)
+ return err;
+
+ data = irq_desc_get_irq_data(desc);
+
+ do {
+ chip = irq_data_get_irq_chip(data);
+ if (chip->irq_set_irqchip_state)
+ break;
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+ data = data->parent_data;
+#else
+ data = NULL;
+#endif
+ } while (data);
+
+ if (data)
+ err = chip->irq_set_irqchip_state(data, which, val);
+
+ irq_put_desc_busunlock(desc, flags);
+ return err;
+}
--
2.1.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 1/3] genirq: Allow the irqchip state of an IRQ to be save/restored
2015-01-07 17:51 ` [PATCH v3 1/3] genirq: Allow the irqchip state of an IRQ to be save/restored Marc Zyngier
@ 2015-02-05 23:33 ` Bjorn Andersson
2015-02-06 9:02 ` Marc Zyngier
0 siblings, 1 reply; 6+ messages in thread
From: Bjorn Andersson @ 2015-02-05 23:33 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, Jan 7, 2015 at 9:51 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
> 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.
>
> Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> Tested-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Any update on the status of this?
I would like to be able to move ahead with the pinctrl driver for the
Qualcomm PM8921 pmic, that depends on this being in place.
Regards,
Bjorn
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 1/3] genirq: Allow the irqchip state of an IRQ to be save/restored
2015-02-05 23:33 ` Bjorn Andersson
@ 2015-02-06 9:02 ` Marc Zyngier
0 siblings, 0 replies; 6+ messages in thread
From: Marc Zyngier @ 2015-02-06 9:02 UTC (permalink / raw)
To: linux-arm-kernel
On 05/02/15 23:33, Bjorn Andersson wrote:
> On Wed, Jan 7, 2015 at 9:51 AM, Marc Zyngier <marc.zyngier@arm.com> wrote:
>> 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.
>>
>> Reviewed-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
>> Tested-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
>> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
>
> Any update on the status of this?
>
> I would like to be able to move ahead with the pinctrl driver for the
> Qualcomm PM8921 pmic, that depends on this being in place.
None so far.
Thomas, do you have any comment on this one?
Thanks,
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 2/3] irqchip: GIC: Add support for irq_{get, set}_irqchip_state
2015-01-07 17:50 [PATCH v3 0/3] genirq: Saving/restoring the irqchip state of an irq line Marc Zyngier
2015-01-07 17:51 ` [PATCH v3 1/3] genirq: Allow the irqchip state of an IRQ to be save/restored Marc Zyngier
@ 2015-01-07 17:51 ` Marc Zyngier
2015-01-07 17:51 ` [PATCH v3 3/3] irqchip: GICv3: " Marc Zyngier
2 siblings, 0 replies; 6+ messages in thread
From: Marc Zyngier @ 2015-01-07 17:51 UTC (permalink / raw)
To: linux-arm-kernel
Add the required hooks for the internal state of an interrupt
to be exposed to other subsystems.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
drivers/irqchip/irq-gic.c | 69 +++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 64 insertions(+), 5 deletions(-)
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index d617ee5..122fb3d 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -151,12 +151,22 @@ static inline unsigned int gic_irq(struct irq_data *d)
/*
* Routines to acknowledge, disable and enable interrupts
*/
-static void gic_mask_irq(struct irq_data *d)
+static void gic_poke_irq(struct irq_data *d, u32 offset)
+{
+ u32 mask = 1 << (gic_irq(d) % 32);
+ writel_relaxed(mask, gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4);
+}
+
+static int gic_peek_irq(struct irq_data *d, u32 offset)
{
u32 mask = 1 << (gic_irq(d) % 32);
+ return !!(readl_relaxed(gic_dist_base(d) + offset + (gic_irq(d) / 32) * 4) & mask);
+}
+static void gic_mask_irq(struct irq_data *d)
+{
raw_spin_lock(&irq_controller_lock);
- writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4);
+ gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR);
if (gic_arch_extn.irq_mask)
gic_arch_extn.irq_mask(d);
raw_spin_unlock(&irq_controller_lock);
@@ -164,12 +174,10 @@ static void gic_mask_irq(struct irq_data *d)
static void gic_unmask_irq(struct irq_data *d)
{
- u32 mask = 1 << (gic_irq(d) % 32);
-
raw_spin_lock(&irq_controller_lock);
if (gic_arch_extn.irq_unmask)
gic_arch_extn.irq_unmask(d);
- writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_SET + (gic_irq(d) / 32) * 4);
+ gic_poke_irq(d, GIC_DIST_ENABLE_SET);
raw_spin_unlock(&irq_controller_lock);
}
@@ -184,6 +192,55 @@ static void gic_eoi_irq(struct irq_data *d)
writel_relaxed(gic_irq(d), gic_cpu_base(d) + GIC_CPU_EOI);
}
+static int gic_irq_set_irqchip_state(struct irq_data *d,
+ enum irqchip_irq_state which, bool val)
+{
+ u32 reg;
+
+ switch (which) {
+ case IRQCHIP_STATE_PENDING:
+ reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR;
+ break;
+
+ case IRQCHIP_STATE_ACTIVE:
+ reg = val ? GIC_DIST_ACTIVE_SET : GIC_DIST_ACTIVE_CLEAR;
+ break;
+
+ case IRQCHIP_STATE_MASKED:
+ reg = val ? GIC_DIST_ENABLE_CLEAR : GIC_DIST_ENABLE_SET;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ gic_poke_irq(d, reg);
+ return 0;
+}
+
+static int gic_irq_get_irqchip_state(struct irq_data *d,
+ enum irqchip_irq_state which, bool *val)
+{
+ switch (which) {
+ case IRQCHIP_STATE_PENDING:
+ *val = gic_peek_irq(d, GIC_DIST_PENDING_SET);
+ break;
+
+ case IRQCHIP_STATE_ACTIVE:
+ *val = gic_peek_irq(d, GIC_DIST_ACTIVE_SET);
+ break;
+
+ case IRQCHIP_STATE_MASKED:
+ *val = !gic_peek_irq(d, GIC_DIST_ENABLE_SET);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int gic_set_type(struct irq_data *d, unsigned int type)
{
void __iomem *base = gic_dist_base(d);
@@ -322,6 +379,8 @@ static struct irq_chip gic_chip = {
.irq_set_affinity = gic_set_affinity,
#endif
.irq_set_wake = gic_set_wake,
+ .irq_get_irqchip_state = gic_irq_get_irqchip_state,
+ .irq_set_irqchip_state = gic_irq_set_irqchip_state,
};
void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
--
2.1.4
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v3 3/3] irqchip: GICv3: Add support for irq_{get, set}_irqchip_state
2015-01-07 17:50 [PATCH v3 0/3] genirq: Saving/restoring the irqchip state of an irq line Marc Zyngier
2015-01-07 17:51 ` [PATCH v3 1/3] genirq: Allow the irqchip state of an IRQ to be save/restored Marc Zyngier
2015-01-07 17:51 ` [PATCH v3 2/3] irqchip: GIC: Add support for irq_{get, set}_irqchip_state Marc Zyngier
@ 2015-01-07 17:51 ` Marc Zyngier
2 siblings, 0 replies; 6+ messages in thread
From: Marc Zyngier @ 2015-01-07 17:51 UTC (permalink / raw)
To: linux-arm-kernel
Add the required hooks for the internal state of an interrupt
to be exposed to other subsystems.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
---
drivers/irqchip/irq-gic-v3.c | 83 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 70 insertions(+), 13 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 1a146cc..6e73846 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -195,6 +195,19 @@ static void gic_enable_redist(bool enable)
/*
* Routines to disable, enable, EOI and route interrupts
*/
+static int gic_peek_irq(struct irq_data *d, u32 offset)
+{
+ u32 mask = 1 << (gic_irq(d) % 32);
+ void __iomem *base;
+
+ if (gic_irq_in_rdist(d))
+ base = gic_data_rdist_sgi_base();
+ else
+ base = gic_data.dist_base;
+
+ return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask);
+}
+
static void gic_poke_irq(struct irq_data *d, u32 offset)
{
u32 mask = 1 << (gic_irq(d) % 32);
@@ -223,6 +236,61 @@ static void gic_unmask_irq(struct irq_data *d)
gic_poke_irq(d, GICD_ISENABLER);
}
+static int gic_irq_set_irqchip_state(struct irq_data *d,
+ enum irqchip_irq_state which, bool val)
+{
+ u32 reg;
+
+ if (d->hwirq >= gic_data.irq_nr) /* PPI/SPI only */
+ return -EINVAL;
+
+ switch (which) {
+ case IRQCHIP_STATE_PENDING:
+ reg = val ? GICD_ISPENDR : GICD_ICPENDR;
+ break;
+
+ case IRQCHIP_STATE_ACTIVE:
+ reg = val ? GICD_ISACTIVER : GICD_ICACTIVER;
+ break;
+
+ case IRQCHIP_STATE_MASKED:
+ reg = val ? GICD_ICENABLER : GICD_ISENABLER;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ gic_poke_irq(d, reg);
+ return 0;
+}
+
+static int gic_irq_get_irqchip_state(struct irq_data *d,
+ enum irqchip_irq_state which, bool *val)
+{
+ if (d->hwirq >= gic_data.irq_nr) /* PPI/SPI only */
+ return -EINVAL;
+
+ switch (which) {
+ case IRQCHIP_STATE_PENDING:
+ *val = gic_peek_irq(d, GICD_ISPENDR);
+ break;
+
+ case IRQCHIP_STATE_ACTIVE:
+ *val = gic_peek_irq(d, GICD_ISACTIVER);
+ break;
+
+ case IRQCHIP_STATE_MASKED:
+ *val = !gic_peek_irq(d, GICD_ISENABLER);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static void gic_eoi_irq(struct irq_data *d)
{
gic_write_eoir(gic_irq(d));
@@ -418,19 +486,6 @@ static void gic_cpu_init(void)
}
#ifdef CONFIG_SMP
-static int gic_peek_irq(struct irq_data *d, u32 offset)
-{
- u32 mask = 1 << (gic_irq(d) % 32);
- void __iomem *base;
-
- if (gic_irq_in_rdist(d))
- base = gic_data_rdist_sgi_base();
- else
- base = gic_data.dist_base;
-
- return !!(readl_relaxed(base + offset + (gic_irq(d) / 32) * 4) & mask);
-}
-
static int gic_secondary_init(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
@@ -597,6 +652,8 @@ static struct irq_chip gic_chip = {
.irq_eoi = gic_eoi_irq,
.irq_set_type = gic_set_type,
.irq_set_affinity = gic_set_affinity,
+ .irq_get_irqchip_state = gic_irq_get_irqchip_state,
+ .irq_set_irqchip_state = gic_irq_set_irqchip_state,
};
#define GIC_ID_NR (1U << gic_data.rdists.id_bits)
--
2.1.4
^ permalink raw reply related [flat|nested] 6+ messages in thread