| 1 | |
| 2 | * Platform information definitions.
|
| 3 | *
|
| 4 | * Copied from arch/ppc/syslib/cpm2_pic.c with minor subsequent updates
|
| 5 | * to make in work in arch/powerpc/. Original (c) belongs to Dan Malek.
|
| 6 | *
|
| 7 | * Author: Vitaly Bordug <[email protected]>
|
| 8 | *
|
| 9 | * 1999-2001 (c) Dan Malek <[email protected]>
|
| 10 | * 2006 (c) MontaVista Software, Inc.
|
| 11 | *
|
| 12 | * This file is licensed under the terms of the GNU General Public License
|
| 13 | * version 2. This program is licensed "as is" without any warranty of any
|
| 14 | * kind, whether express or implied.
|
| 15 |
|
| 16 |
|
| 17 | |
| 18 | * the only interrupt controller.
|
| 19 | * There are two 32-bit registers (high/low) for up to 64
|
| 20 | * possible interrupts.
|
| 21 | *
|
| 22 | * Now, the fun starts.....Interrupt Numbers DO NOT MAP
|
| 23 | * in a simple arithmetic fashion to mask or pending registers.
|
| 24 | * That is, interrupt 4 does not map to bit position 4.
|
| 25 | * We create two tables, indexed by vector number, to indicate
|
| 26 | * which register to use and which bit in the register to use.
|
| 27 |
|
| 28 |
|
| 29 | #include <linux/stddef.h>
|
| 30 | #include <linux/sched.h>
|
| 31 | #include <linux/signal.h>
|
| 32 | #include <linux/irq.h>
|
| 33 | #include <linux/irqdomain.h>
|
| 34 |
|
| 35 | #include <asm/immap_cpm2.h>
|
| 36 | #include <asm/io.h>
|
| 37 |
|
| 38 | #include "cpm2_pic.h"
|
| 39 |
|
| 40 |
|
| 41 | #define CPM2_IRQ_EXT1 19
|
| 42 | #define CPM2_IRQ_EXT7 25
|
| 43 |
|
| 44 |
|
| 45 | #define CPM2_IRQ_PORTC15 48
|
| 46 | #define CPM2_IRQ_PORTC0 63
|
| 47 |
|
| 48 | static intctl_cpm2_t __iomem *cpm2_intctl;
|
| 49 |
|
| 50 | static struct irq_domain *cpm2_pic_host;
|
| 51 | static unsigned long ppc_cached_irq_mask[2];
|
| 52 |
|
| 53 | static const u_char irq_to_siureg[] = {
|
| 54 | 1, 1, 1, 1, 1, 1, 1, 1,
|
| 55 | 1, 1, 1, 1, 1, 1, 1, 1,
|
| 56 | 0, 0, 0, 0, 0, 0, 0, 0,
|
| 57 | 0, 0, 0, 0, 0, 0, 0, 0,
|
| 58 | 1, 1, 1, 1, 1, 1, 1, 1,
|
| 59 | 1, 1, 1, 1, 1, 1, 1, 1,
|
| 60 | 0, 0, 0, 0, 0, 0, 0, 0,
|
| 61 | 0, 0, 0, 0, 0, 0, 0, 0
|
| 62 | };
|
| 63 |
|
| 64 | |
| 65 | * a given irq is (1 << irq_to_siubit[irq])
|
| 66 | static const u_char irq_to_siubit[] = {
|
| 67 | 0, 15, 14, 13, 12, 11, 10, 9,
|
| 68 | 8, 7, 6, 5, 4, 3, 2, 1,
|
| 69 | 2, 1, 0, 14, 13, 12, 11, 10,
|
| 70 | 9, 8, 7, 6, 5, 4, 3, 0,
|
| 71 | 31, 30, 29, 28, 27, 26, 25, 24,
|
| 72 | 23, 22, 21, 20, 19, 18, 17, 16,
|
| 73 | 16, 17, 18, 19, 20, 21, 22, 23,
|
| 74 | 24, 25, 26, 27, 28, 29, 30, 31,
|
| 75 | };
|
| 76 |
|
| 77 | static void cpm2_mask_irq(struct irq_data *d)
|
| 78 | {
|
| 79 | int bit, word;
|
| 80 | unsigned int irq_nr = irqd_to_hwirq(d);
|
| 81 |
|
| 82 | bit = irq_to_siubit[irq_nr];
|
| 83 | word = irq_to_siureg[irq_nr];
|
| 84 |
|
| 85 | ppc_cached_irq_mask[word] &= ~(1 << bit);
|
| 86 | out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
|
| 87 | }
|
| 88 |
|
| 89 | static void cpm2_unmask_irq(struct irq_data *d)
|
| 90 | {
|
| 91 | int bit, word;
|
| 92 | unsigned int irq_nr = irqd_to_hwirq(d);
|
| 93 |
|
| 94 | bit = irq_to_siubit[irq_nr];
|
| 95 | word = irq_to_siureg[irq_nr];
|
| 96 |
|
| 97 | ppc_cached_irq_mask[word] |= 1 << bit;
|
| 98 | out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
|
| 99 | }
|
| 100 |
|
| 101 | static void cpm2_ack(struct irq_data *d)
|
| 102 | {
|
| 103 | int bit, word;
|
| 104 | unsigned int irq_nr = irqd_to_hwirq(d);
|
| 105 |
|
| 106 | bit = irq_to_siubit[irq_nr];
|
| 107 | word = irq_to_siureg[irq_nr];
|
| 108 |
|
| 109 | out_be32(&cpm2_intctl->ic_sipnrh + word, 1 << bit);
|
| 110 | }
|
| 111 |
|
| 112 | static void cpm2_end_irq(struct irq_data *d)
|
| 113 | {
|
| 114 | int bit, word;
|
| 115 | unsigned int irq_nr = irqd_to_hwirq(d);
|
| 116 |
|
| 117 | bit = irq_to_siubit[irq_nr];
|
| 118 | word = irq_to_siureg[irq_nr];
|
| 119 |
|
| 120 | ppc_cached_irq_mask[word] |= 1 << bit;
|
| 121 | out_be32(&cpm2_intctl->ic_simrh + word, ppc_cached_irq_mask[word]);
|
| 122 |
|
| 123 | |
| 124 | * Work around large numbers of spurious IRQs on PowerPC 82xx
|
| 125 | * systems.
|
| 126 |
|
| 127 | mb();
|
| 128 | }
|
| 129 |
|
| 130 | static int cpm2_set_irq_type(struct irq_data *d, unsigned int flow_type)
|
| 131 | {
|
| 132 | unsigned int src = irqd_to_hwirq(d);
|
| 133 | unsigned int vold, vnew, edibit;
|
| 134 |
|
| 135 | |
| 136 | * IRQ_TYPE_EDGE_BOTH (default). All others are IRQ_TYPE_EDGE_FALLING
|
| 137 | * or IRQ_TYPE_LEVEL_LOW (default)
|
| 138 |
|
| 139 | if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0) {
|
| 140 | if (flow_type == IRQ_TYPE_NONE)
|
| 141 | flow_type = IRQ_TYPE_EDGE_BOTH;
|
| 142 |
|
| 143 | if (flow_type != IRQ_TYPE_EDGE_BOTH &&
|
| 144 | flow_type != IRQ_TYPE_EDGE_FALLING)
|
| 145 | goto err_sense;
|
| 146 | } else {
|
| 147 | if (flow_type == IRQ_TYPE_NONE)
|
| 148 | flow_type = IRQ_TYPE_LEVEL_LOW;
|
| 149 |
|
| 150 | if (flow_type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
|
| 151 | goto err_sense;
|
| 152 | }
|
| 153 |
|
| 154 | irqd_set_trigger_type(d, flow_type);
|
| 155 | if (flow_type & IRQ_TYPE_LEVEL_LOW)
|
| 156 | irq_set_handler_locked(d, handle_level_irq);
|
| 157 | else
|
| 158 | irq_set_handler_locked(d, handle_edge_irq);
|
| 159 |
|
| 160 | |
| 161 | * EXT IRQ and Port C IRQ senses are programmable
|
| 162 |
|
| 163 | if (src >= CPM2_IRQ_EXT1 && src <= CPM2_IRQ_EXT7)
|
| 164 | edibit = (14 - (src - CPM2_IRQ_EXT1));
|
| 165 | else
|
| 166 | if (src >= CPM2_IRQ_PORTC15 && src <= CPM2_IRQ_PORTC0)
|
| 167 | edibit = (31 - (CPM2_IRQ_PORTC0 - src));
|
| 168 | else
|
| 169 | return (flow_type & IRQ_TYPE_LEVEL_LOW) ?
|
| 170 | IRQ_SET_MASK_OK_NOCOPY : -EINVAL;
|
| 171 |
|
| 172 | vold = in_be32(&cpm2_intctl->ic_siexr);
|
| 173 |
|
| 174 | if ((flow_type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_FALLING)
|
| 175 | vnew = vold | (1 << edibit);
|
| 176 | else
|
| 177 | vnew = vold & ~(1 << edibit);
|
| 178 |
|
| 179 | if (vold != vnew)
|
| 180 | out_be32(&cpm2_intctl->ic_siexr, vnew);
|
| 181 | return IRQ_SET_MASK_OK_NOCOPY;
|
| 182 |
|
| 183 | err_sense:
|
| 184 | pr_err("CPM2 PIC: sense type 0x%x not supported\n", flow_type);
|
| 185 | return -EINVAL;
|
| 186 | }
|
| 187 |
|
| 188 | static struct irq_chip cpm2_pic = {
|
| 189 | .name = "CPM2 SIU",
|
| 190 | .irq_mask = cpm2_mask_irq,
|
| 191 | .irq_unmask = cpm2_unmask_irq,
|
| 192 | .irq_ack = cpm2_ack,
|
| 193 | .irq_eoi = cpm2_end_irq,
|
| 194 | .irq_set_type = cpm2_set_irq_type,
|
| 195 | .flags = IRQCHIP_EOI_IF_HANDLED,
|
| 196 | };
|
| 197 |
|
| 198 | unsigned int cpm2_get_irq(void)
|
| 199 | {
|
| 200 | int irq;
|
| 201 | unsigned long bits;
|
| 202 |
|
| 203 | |
| 204 | * to get the irq number.
|
| 205 | bits = in_be32(&cpm2_intctl->ic_sivec);
|
| 206 | irq = bits >> 26;
|
| 207 |
|
| 208 | if (irq == 0)
|
| 209 | return(-1);
|
| 210 | return irq_find_mapping(cpm2_pic_host, irq);
|
| 211 | }
|
| 212 |
|
| 213 | static int cpm2_pic_host_map(struct irq_domain *h, unsigned int virq,
|
| 214 | irq_hw_number_t hw)
|
| 215 | {
|
| 216 | pr_debug("cpm2_pic_host_map(%d, 0x%lx)\n", virq, hw);
|
| 217 |
|
| 218 | irq_set_status_flags(virq, IRQ_LEVEL);
|
| 219 | irq_set_chip_and_handler(virq, &cpm2_pic, handle_level_irq);
|
| 220 | return 0;
|
| 221 | }
|
| 222 |
|
| 223 | static const struct irq_domain_ops cpm2_pic_host_ops = {
|
| 224 | .map = cpm2_pic_host_map,
|
| 225 | .xlate = irq_domain_xlate_onetwocell,
|
| 226 | };
|
| 227 |
|
| 228 | void cpm2_pic_init(struct device_node *node)
|
| 229 | {
|
| 230 | int i;
|
| 231 |
|
| 232 | cpm2_intctl = &cpm2_immr->im_intctl;
|
| 233 |
|
| 234 | |
| 235 | * from the bootloader
|
| 236 |
|
| 237 |
|
| 238 |
|
| 239 |
|
| 240 | out_be32(&cpm2_intctl->ic_simrh, 0x00000000);
|
| 241 | out_be32(&cpm2_intctl->ic_simrl, 0x00000000);
|
| 242 |
|
| 243 | wmb();
|
| 244 |
|
| 245 |
|
| 246 | out_be32(&cpm2_intctl->ic_sipnrh, 0xffffffff);
|
| 247 | out_be32(&cpm2_intctl->ic_sipnrl, 0xffffffff);
|
| 248 | wmb();
|
| 249 |
|
| 250 |
|
| 251 | i = in_be32(&cpm2_intctl->ic_sivec);
|
| 252 | rmb();
|
| 253 |
|
| 254 | |
| 255 | * in case the boot rom changed something on us.
|
| 256 |
|
| 257 | out_be16(&cpm2_intctl->ic_sicr, 0);
|
| 258 | out_be32(&cpm2_intctl->ic_scprrh, 0x05309770);
|
| 259 | out_be32(&cpm2_intctl->ic_scprrl, 0x05309770);
|
| 260 |
|
| 261 |
|
| 262 | cpm2_pic_host = irq_domain_create_linear(of_fwnode_handle(node), 64,
|
| 263 | &cpm2_pic_host_ops, NULL);
|
| 264 | if (cpm2_pic_host == NULL) {
|
| 265 | printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
|
| 266 | return;
|
| 267 | }
|
| 268 | }
|