diff options
Diffstat (limited to 'arch/powerpc/sysdev/xilinx_intc.c')
-rw-r--r-- | arch/powerpc/sysdev/xilinx_intc.c | 117 |
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 | ||
42 | static struct irq_host *master_irqhost; | 42 | static 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 | */ | ||
50 | static 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 | */ | ||
55 | static 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 | */ |
47 | static void xilinx_intc_mask(unsigned int virq) | 71 | static 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 | ||
55 | static void xilinx_intc_unmask(unsigned int virq) | 79 | static 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 | */ | ||
93 | static 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 | ||
63 | static void xilinx_intc_ack(unsigned int virq) | 107 | static 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 | */ | ||
118 | static 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 | |||
126 | static 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 | ||
71 | static struct irq_chip xilinx_intc_irqchip = { | 134 | static 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 | */ | ||
149 | static 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 | } | ||
81 | static int xilinx_intc_map(struct irq_host *h, unsigned int virq, | 169 | static 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 | ||
90 | static struct irq_host_ops xilinx_intc_ops = { | 185 | static 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 | ||
94 | struct irq_host * __init | 190 | struct 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; |