* [PATCH] Powerpc: Xilinx: Intc: Fix level irqs which have duplicates
@ 2009-04-03 22:55 John Linn
2009-04-12 6:18 ` Grant Likely
0 siblings, 1 reply; 2+ messages in thread
From: John Linn @ 2009-04-03 22:55 UTC (permalink / raw)
To: grant.likely, linuxppc-dev, jwboyer; +Cc: John Linn
The interrupt controller was not handling level interrupts correctly
such that duplicate interrupts were happening. This fixes the problem
and adds edge type interrupts which are needed in Xilinx hardware.
Signed-off-by: John Linn <john.linn@xilinx.com>
---
arch/powerpc/sysdev/xilinx_intc.c | 112 ++++++++++++++++++++++++++++++++++---
1 files changed, 103 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c
index a22e1a2..7c99a1e 100644
--- a/arch/powerpc/sysdev/xilinx_intc.c
+++ b/arch/powerpc/sysdev/xilinx_intc.c
@@ -41,8 +41,30 @@
static struct irq_host *master_irqhost;
+/* The following table allows the interrupt type, edge or level,
+ * to be cached after being read from the device tree until the interrupt
+ * is mapped
+ */
+static int xilinx_intc_typetable[32];
+
+/* Map the interrupt type from the device tree to the interrupt types
+ * used by the interrupt subsystem
+ */
+static unsigned char xilinx_intc_map_senses[] = {
+ IRQ_TYPE_EDGE_RISING,
+ IRQ_TYPE_EDGE_FALLING,
+ IRQ_TYPE_LEVEL_HIGH,
+ IRQ_TYPE_LEVEL_LOW,
+};
+
/*
- * IRQ Chip operations
+ * The interrupt controller is setup such that it doesn't work well with
+ * the level interrupt handler in the kernel because the handler acks the
+ * interrupt before calling the application interrupt handler. To deal with
+ * that, we use 2 different irq chips so that different functions can be
+ * used for level and edge type interrupts.
+ *
+ * IRQ Chip common (across level and edge) operations
*/
static void xilinx_intc_mask(unsigned int virq)
{
@@ -52,15 +74,54 @@ static void xilinx_intc_mask(unsigned int virq)
out_be32(regs + XINTC_CIE, 1 << irq);
}
-static void xilinx_intc_unmask(unsigned int virq)
+static int xilinx_intc_set_type(unsigned int virq, unsigned int flow_type)
+{
+ struct irq_desc *desc = get_irq_desc(virq);
+
+ desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
+ desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
+ if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
+ desc->status |= IRQ_LEVEL;
+ return 0;
+}
+
+/*
+ * IRQ Chip level operations
+ */
+static void xilinx_intc_level_unmask(unsigned int virq)
{
int irq = virq_to_hw(virq);
void * regs = get_irq_chip_data(virq);
pr_debug("unmask: %d\n", irq);
out_be32(regs + XINTC_SIE, 1 << irq);
+
+ /* ack level irqs because they can't be acked during
+ * ack function since the handle_level_irq function
+ * acks the irq before calling the inerrupt handler
+ */
+ out_be32(regs + XINTC_IAR, 1 << irq);
+}
+
+static struct irq_chip xilinx_intc_level_irqchip = {
+ .typename = "Xilinx Level INTC",
+ .mask = xilinx_intc_mask,
+ .mask_ack = xilinx_intc_mask,
+ .unmask = xilinx_intc_level_unmask,
+ .set_type = xilinx_intc_set_type,
+};
+
+/*
+ * IRQ Chip edge operations
+ */
+static void xilinx_intc_edge_unmask(unsigned int virq)
+{
+ int irq = virq_to_hw(virq);
+ void *regs = get_irq_chip_data(virq);
+ pr_debug("unmask: %d\n", irq);
+ out_be32(regs + XINTC_SIE, 1 << irq);
}
-static void xilinx_intc_ack(unsigned int virq)
+static void xilinx_intc_edge_ack(unsigned int virq)
{
int irq = virq_to_hw(virq);
void * regs = get_irq_chip_data(virq);
@@ -68,27 +129,60 @@ static void xilinx_intc_ack(unsigned int virq)
out_be32(regs + XINTC_IAR, 1 << irq);
}
-static struct irq_chip xilinx_intc_irqchip = {
- .typename = "Xilinx INTC",
+static struct irq_chip xilinx_intc_edge_irqchip = {
+ .typename = "Xilinx Edge INTC",
.mask = xilinx_intc_mask,
- .unmask = xilinx_intc_unmask,
- .ack = xilinx_intc_ack,
+ .unmask = xilinx_intc_edge_unmask,
+ .ack = xilinx_intc_edge_ack,
+ .set_type = xilinx_intc_set_type,
};
/*
* IRQ Host operations
*/
+
+/**
+ * xilinx_intc_xlate - translate virq# from device tree interrupts property
+ */
+static int xilinx_intc_xlate(struct irq_host *h, struct device_node *ct,
+ u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_flags)
+{
+ if (intsize != 2)
+ return -1;
+
+ /* keep a copy of the interrupt type til the interrupt is mapped
+ */
+ xilinx_intc_typetable[intspec[0]] = xilinx_intc_map_senses[intspec[1]];
+
+ /* Xilinx uses 2 interrupt entries, the 1st being the h/w
+ * interrupt number, the 2nd being the interrupt type, edge or level
+ */
+ *out_hwirq = intspec[0];
+ *out_flags = xilinx_intc_map_senses[intspec[1]];
+
+ return 0;
+}
static int xilinx_intc_map(struct irq_host *h, unsigned int virq,
irq_hw_number_t irq)
{
set_irq_chip_data(virq, h->host_data);
- set_irq_chip_and_handler(virq, &xilinx_intc_irqchip, handle_level_irq);
- set_irq_type(virq, IRQ_TYPE_NONE);
+
+ if (xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_HIGH ||
+ xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_LOW) {
+ set_irq_chip_and_handler(virq, &xilinx_intc_level_irqchip,
+ handle_level_irq);
+ } else {
+ set_irq_chip_and_handler(virq, &xilinx_intc_edge_irqchip,
+ handle_edge_irq);
+ }
return 0;
}
static struct irq_host_ops xilinx_intc_ops = {
.map = xilinx_intc_map,
+ .xlate = xilinx_intc_xlate,
};
struct irq_host * __init
--
1.6.2.1
This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH] Powerpc: Xilinx: Intc: Fix level irqs which have duplicates
2009-04-03 22:55 [PATCH] Powerpc: Xilinx: Intc: Fix level irqs which have duplicates John Linn
@ 2009-04-12 6:18 ` Grant Likely
0 siblings, 0 replies; 2+ messages in thread
From: Grant Likely @ 2009-04-12 6:18 UTC (permalink / raw)
To: John Linn; +Cc: linuxppc-dev
I think this looks good. I'll need to pull it into my tree and try it out.
g.
On Fri, Apr 3, 2009 at 4:55 PM, John Linn <john.linn@xilinx.com> wrote:
> The interrupt controller was not handling level interrupts correctly
> such that duplicate interrupts were happening. This fixes the problem
> and adds edge type interrupts which are needed in Xilinx hardware.
>
> Signed-off-by: John Linn <john.linn@xilinx.com>
> ---
> =A0arch/powerpc/sysdev/xilinx_intc.c | =A0112 +++++++++++++++++++++++++++=
+++++++---
> =A01 files changed, 103 insertions(+), 9 deletions(-)
>
> diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xili=
nx_intc.c
> index a22e1a2..7c99a1e 100644
> --- a/arch/powerpc/sysdev/xilinx_intc.c
> +++ b/arch/powerpc/sysdev/xilinx_intc.c
> @@ -41,8 +41,30 @@
>
> =A0static struct irq_host *master_irqhost;
>
> +/* The following table allows the interrupt type, edge or level,
> + * to be cached after being read from the device tree until the interrup=
t
> + * is mapped
> + */
> +static int xilinx_intc_typetable[32];
> +
> +/* Map the interrupt type from the device tree to the interrupt types
> + * used by the interrupt subsystem
> + */
> +static unsigned char xilinx_intc_map_senses[] =3D {
> + =A0 =A0 =A0 IRQ_TYPE_EDGE_RISING,
> + =A0 =A0 =A0 IRQ_TYPE_EDGE_FALLING,
> + =A0 =A0 =A0 IRQ_TYPE_LEVEL_HIGH,
> + =A0 =A0 =A0 IRQ_TYPE_LEVEL_LOW,
> +};
> +
> =A0/*
> - * IRQ Chip operations
> + * The interrupt controller is setup such that it doesn't work well with
> + * the level interrupt handler in the kernel because the handler acks th=
e
> + * interrupt before calling the application interrupt handler. To deal w=
ith
> + * that, we use 2 different irq chips so that different functions can be
> + * used for level and edge type interrupts.
> + *
> + * IRQ Chip common (across level and edge) operations
> =A0*/
> =A0static void xilinx_intc_mask(unsigned int virq)
> =A0{
> @@ -52,15 +74,54 @@ static void xilinx_intc_mask(unsigned int virq)
> =A0 =A0 =A0 =A0out_be32(regs + XINTC_CIE, 1 << irq);
> =A0}
>
> -static void xilinx_intc_unmask(unsigned int virq)
> +static int xilinx_intc_set_type(unsigned int virq, unsigned int flow_typ=
e)
> +{
> + =A0 =A0 =A0 struct irq_desc *desc =3D get_irq_desc(virq);
> +
> + =A0 =A0 =A0 desc->status &=3D ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
> + =A0 =A0 =A0 desc->status |=3D flow_type & IRQ_TYPE_SENSE_MASK;
> + =A0 =A0 =A0 if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 desc->status |=3D IRQ_LEVEL;
> + =A0 =A0 =A0 return 0;
> +}
> +
> +/*
> + * IRQ Chip level operations
> + */
> +static void xilinx_intc_level_unmask(unsigned int virq)
> =A0{
> =A0 =A0 =A0 =A0int irq =3D virq_to_hw(virq);
> =A0 =A0 =A0 =A0void * regs =3D get_irq_chip_data(virq);
> =A0 =A0 =A0 =A0pr_debug("unmask: %d\n", irq);
> =A0 =A0 =A0 =A0out_be32(regs + XINTC_SIE, 1 << irq);
> +
> + =A0 =A0 =A0 /* ack level irqs because they can't be acked during
> + =A0 =A0 =A0 =A0* ack function since the handle_level_irq function
> + =A0 =A0 =A0 =A0* acks the irq before calling the inerrupt handler
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 out_be32(regs + XINTC_IAR, 1 << irq);
> +}
> +
> +static struct irq_chip xilinx_intc_level_irqchip =3D {
> + =A0 =A0 =A0 .typename =3D "Xilinx Level INTC",
> + =A0 =A0 =A0 .mask =3D xilinx_intc_mask,
> + =A0 =A0 =A0 .mask_ack =3D xilinx_intc_mask,
> + =A0 =A0 =A0 .unmask =3D xilinx_intc_level_unmask,
> + =A0 =A0 =A0 .set_type =3D xilinx_intc_set_type,
> +};
> +
> +/*
> + * IRQ Chip edge operations
> + */
> +static void xilinx_intc_edge_unmask(unsigned int virq)
> +{
> + =A0 =A0 =A0 int irq =3D virq_to_hw(virq);
> + =A0 =A0 =A0 void *regs =3D get_irq_chip_data(virq);
> + =A0 =A0 =A0 pr_debug("unmask: %d\n", irq);
> + =A0 =A0 =A0 out_be32(regs + XINTC_SIE, 1 << irq);
> =A0}
>
> -static void xilinx_intc_ack(unsigned int virq)
> +static void xilinx_intc_edge_ack(unsigned int virq)
> =A0{
> =A0 =A0 =A0 =A0int irq =3D virq_to_hw(virq);
> =A0 =A0 =A0 =A0void * regs =3D get_irq_chip_data(virq);
> @@ -68,27 +129,60 @@ static void xilinx_intc_ack(unsigned int virq)
> =A0 =A0 =A0 =A0out_be32(regs + XINTC_IAR, 1 << irq);
> =A0}
>
> -static struct irq_chip xilinx_intc_irqchip =3D {
> - =A0 =A0 =A0 .typename =3D "Xilinx INTC",
> +static struct irq_chip xilinx_intc_edge_irqchip =3D {
> + =A0 =A0 =A0 .typename =3D "Xilinx Edge =A0INTC",
> =A0 =A0 =A0 =A0.mask =3D xilinx_intc_mask,
> - =A0 =A0 =A0 .unmask =3D xilinx_intc_unmask,
> - =A0 =A0 =A0 .ack =3D xilinx_intc_ack,
> + =A0 =A0 =A0 .unmask =3D xilinx_intc_edge_unmask,
> + =A0 =A0 =A0 .ack =3D xilinx_intc_edge_ack,
> + =A0 =A0 =A0 .set_type =3D xilinx_intc_set_type,
> =A0};
>
> =A0/*
> =A0* IRQ Host operations
> =A0*/
> +
> +/**
> + * xilinx_intc_xlate - translate virq# from device tree interrupts prope=
rty
> + */
> +static int xilinx_intc_xlate(struct irq_host *h, struct device_node *ct,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 u32 *intspe=
c, unsigned int intsize,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq_hw_numb=
er_t *out_hwirq,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 unsigned in=
t *out_flags)
> +{
> + =A0 =A0 =A0 if (intsize !=3D 2)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -1;
> +
> + =A0 =A0 =A0 /* keep a copy of the interrupt type til the interrupt is m=
apped
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 xilinx_intc_typetable[intspec[0]] =3D xilinx_intc_map_sense=
s[intspec[1]];
> +
> + =A0 =A0 =A0 /* Xilinx uses 2 interrupt entries, the 1st being the h/w
> + =A0 =A0 =A0 =A0* interrupt number, the 2nd being the interrupt type, ed=
ge or level
> + =A0 =A0 =A0 =A0*/
> + =A0 =A0 =A0 *out_hwirq =3D intspec[0];
> + =A0 =A0 =A0 *out_flags =3D xilinx_intc_map_senses[intspec[1]];
> +
> + =A0 =A0 =A0 return 0;
> +}
> =A0static int xilinx_intc_map(struct irq_host *h, unsigned int virq,
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0irq_hw=
_number_t irq)
> =A0{
> =A0 =A0 =A0 =A0set_irq_chip_data(virq, h->host_data);
> - =A0 =A0 =A0 set_irq_chip_and_handler(virq, &xilinx_intc_irqchip, handle=
_level_irq);
> - =A0 =A0 =A0 set_irq_type(virq, IRQ_TYPE_NONE);
> +
> + =A0 =A0 =A0 if (xilinx_intc_typetable[irq] =3D=3D IRQ_TYPE_LEVEL_HIGH |=
|
> + =A0 =A0 =A0 =A0 =A0 xilinx_intc_typetable[irq] =3D=3D IRQ_TYPE_LEVEL_LO=
W) {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 set_irq_chip_and_handler(virq, &xilinx_intc=
_level_irqchip,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_level_irq);
> + =A0 =A0 =A0 } else {
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 set_irq_chip_and_handler(virq, &xilinx_intc=
_edge_irqchip,
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 handle_edge_irq);
> + =A0 =A0 =A0 }
> =A0 =A0 =A0 =A0return 0;
> =A0}
>
> =A0static struct irq_host_ops xilinx_intc_ops =3D {
> =A0 =A0 =A0 =A0.map =3D xilinx_intc_map,
> + =A0 =A0 =A0 .xlate =3D xilinx_intc_xlate,
> =A0};
>
> =A0struct irq_host * __init
> --
> 1.6.2.1
>
>
>
> This email and any attachments are intended for the sole use of the named=
recipient(s) and contain(s) confidential information that may be proprieta=
ry, privileged or copyrighted under applicable law. If you are not the inte=
nded recipient, do not read, copy, or forward this email message or any att=
achments. Delete this email message and any attachments immediately.
>
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2009-04-12 6:18 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-04-03 22:55 [PATCH] Powerpc: Xilinx: Intc: Fix level irqs which have duplicates John Linn
2009-04-12 6:18 ` Grant Likely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox