aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/xilinx_intc.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/sysdev/xilinx_intc.c')
-rw-r--r--arch/powerpc/sysdev/xilinx_intc.c117
1 files changed, 107 insertions, 10 deletions
diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c
index a22e1a2df1af..c658b413c9b4 100644
--- a/arch/powerpc/sysdev/xilinx_intc.c
+++ b/arch/powerpc/sysdev/xilinx_intc.c
@@ -41,8 +41,32 @@
41 41
42static struct irq_host *master_irqhost; 42static struct irq_host *master_irqhost;
43 43
44#define XILINX_INTC_MAXIRQS (32)
45
46/* The following table allows the interrupt type, edge or level,
47 * to be cached after being read from the device tree until the interrupt
48 * is mapped
49 */
50static int xilinx_intc_typetable[XILINX_INTC_MAXIRQS];
51
52/* Map the interrupt type from the device tree to the interrupt types
53 * used by the interrupt subsystem
54 */
55static unsigned char xilinx_intc_map_senses[] = {
56 IRQ_TYPE_EDGE_RISING,
57 IRQ_TYPE_EDGE_FALLING,
58 IRQ_TYPE_LEVEL_HIGH,
59 IRQ_TYPE_LEVEL_LOW,
60};
61
44/* 62/*
45 * IRQ Chip operations 63 * The interrupt controller is setup such that it doesn't work well with
64 * the level interrupt handler in the kernel because the handler acks the
65 * interrupt before calling the application interrupt handler. To deal with
66 * that, we use 2 different irq chips so that different functions can be
67 * used for level and edge type interrupts.
68 *
69 * IRQ Chip common (across level and edge) operations
46 */ 70 */
47static void xilinx_intc_mask(unsigned int virq) 71static void xilinx_intc_mask(unsigned int virq)
48{ 72{
@@ -52,15 +76,54 @@ static void xilinx_intc_mask(unsigned int virq)
52 out_be32(regs + XINTC_CIE, 1 << irq); 76 out_be32(regs + XINTC_CIE, 1 << irq);
53} 77}
54 78
55static void xilinx_intc_unmask(unsigned int virq) 79static int xilinx_intc_set_type(unsigned int virq, unsigned int flow_type)
80{
81 struct irq_desc *desc = get_irq_desc(virq);
82
83 desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
84 desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
85 if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
86 desc->status |= IRQ_LEVEL;
87 return 0;
88}
89
90/*
91 * IRQ Chip level operations
92 */
93static void xilinx_intc_level_unmask(unsigned int virq)
56{ 94{
57 int irq = virq_to_hw(virq); 95 int irq = virq_to_hw(virq);
58 void * regs = get_irq_chip_data(virq); 96 void * regs = get_irq_chip_data(virq);
59 pr_debug("unmask: %d\n", irq); 97 pr_debug("unmask: %d\n", irq);
60 out_be32(regs + XINTC_SIE, 1 << irq); 98 out_be32(regs + XINTC_SIE, 1 << irq);
99
100 /* ack level irqs because they can't be acked during
101 * ack function since the handle_level_irq function
102 * acks the irq before calling the inerrupt handler
103 */
104 out_be32(regs + XINTC_IAR, 1 << irq);
61} 105}
62 106
63static void xilinx_intc_ack(unsigned int virq) 107static struct irq_chip xilinx_intc_level_irqchip = {
108 .typename = "Xilinx Level INTC",
109 .mask = xilinx_intc_mask,
110 .mask_ack = xilinx_intc_mask,
111 .unmask = xilinx_intc_level_unmask,
112 .set_type = xilinx_intc_set_type,
113};
114
115/*
116 * IRQ Chip edge operations
117 */
118static void xilinx_intc_edge_unmask(unsigned int virq)
119{
120 int irq = virq_to_hw(virq);
121 void *regs = get_irq_chip_data(virq);
122 pr_debug("unmask: %d\n", irq);
123 out_be32(regs + XINTC_SIE, 1 << irq);
124}
125
126static void xilinx_intc_edge_ack(unsigned int virq)
64{ 127{
65 int irq = virq_to_hw(virq); 128 int irq = virq_to_hw(virq);
66 void * regs = get_irq_chip_data(virq); 129 void * regs = get_irq_chip_data(virq);
@@ -68,27 +131,60 @@ static void xilinx_intc_ack(unsigned int virq)
68 out_be32(regs + XINTC_IAR, 1 << irq); 131 out_be32(regs + XINTC_IAR, 1 << irq);
69} 132}
70 133
71static struct irq_chip xilinx_intc_irqchip = { 134static struct irq_chip xilinx_intc_edge_irqchip = {
72 .typename = "Xilinx INTC", 135 .typename = "Xilinx Edge INTC",
73 .mask = xilinx_intc_mask, 136 .mask = xilinx_intc_mask,
74 .unmask = xilinx_intc_unmask, 137 .unmask = xilinx_intc_edge_unmask,
75 .ack = xilinx_intc_ack, 138 .ack = xilinx_intc_edge_ack,
139 .set_type = xilinx_intc_set_type,
76}; 140};
77 141
78/* 142/*
79 * IRQ Host operations 143 * IRQ Host operations
80 */ 144 */
145
146/**
147 * xilinx_intc_xlate - translate virq# from device tree interrupts property
148 */
149static int xilinx_intc_xlate(struct irq_host *h, struct device_node *ct,
150 u32 *intspec, unsigned int intsize,
151 irq_hw_number_t *out_hwirq,
152 unsigned int *out_flags)
153{
154 if ((intsize < 2) || (intspec[0] >= XILINX_INTC_MAXIRQS))
155 return -EINVAL;
156
157 /* keep a copy of the interrupt type til the interrupt is mapped
158 */
159 xilinx_intc_typetable[intspec[0]] = xilinx_intc_map_senses[intspec[1]];
160
161 /* Xilinx uses 2 interrupt entries, the 1st being the h/w
162 * interrupt number, the 2nd being the interrupt type, edge or level
163 */
164 *out_hwirq = intspec[0];
165 *out_flags = xilinx_intc_map_senses[intspec[1]];
166
167 return 0;
168}
81static int xilinx_intc_map(struct irq_host *h, unsigned int virq, 169static int xilinx_intc_map(struct irq_host *h, unsigned int virq,
82 irq_hw_number_t irq) 170 irq_hw_number_t irq)
83{ 171{
84 set_irq_chip_data(virq, h->host_data); 172 set_irq_chip_data(virq, h->host_data);
85 set_irq_chip_and_handler(virq, &xilinx_intc_irqchip, handle_level_irq); 173
86 set_irq_type(virq, IRQ_TYPE_NONE); 174 if (xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_HIGH ||
175 xilinx_intc_typetable[irq] == IRQ_TYPE_LEVEL_LOW) {
176 set_irq_chip_and_handler(virq, &xilinx_intc_level_irqchip,
177 handle_level_irq);
178 } else {
179 set_irq_chip_and_handler(virq, &xilinx_intc_edge_irqchip,
180 handle_edge_irq);
181 }
87 return 0; 182 return 0;
88} 183}
89 184
90static struct irq_host_ops xilinx_intc_ops = { 185static struct irq_host_ops xilinx_intc_ops = {
91 .map = xilinx_intc_map, 186 .map = xilinx_intc_map,
187 .xlate = xilinx_intc_xlate,
92}; 188};
93 189
94struct irq_host * __init 190struct irq_host * __init
@@ -116,7 +212,8 @@ xilinx_intc_init(struct device_node *np)
116 out_be32(regs + XINTC_MER, 0x3UL); /* Turn on the Master Enable. */ 212 out_be32(regs + XINTC_MER, 0x3UL); /* Turn on the Master Enable. */
117 213
118 /* Allocate and initialize an irq_host structure. */ 214 /* Allocate and initialize an irq_host structure. */
119 irq = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, 32, &xilinx_intc_ops, -1); 215 irq = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, XILINX_INTC_MAXIRQS,
216 &xilinx_intc_ops, -1);
120 if (!irq) 217 if (!irq)
121 panic(__FILE__ ": Cannot allocate IRQ host\n"); 218 panic(__FILE__ ": Cannot allocate IRQ host\n");
122 irq->host_data = regs; 219 irq->host_data = regs;