Commit | Line | Data |
---|---|---|
1879f711 JR |
1 | /* |
2 | * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved. | |
3 | * | |
4 | * Author: John Rigby, <jrigby@freescale.com> | |
5 | * | |
6 | * Description: | |
7 | * MPC5121ADS CPLD irq handling | |
8 | * | |
9 | * This is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | */ | |
14 | ||
15 | #undef DEBUG | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/irq.h> | |
20 | #include <linux/io.h> | |
21 | #include <asm/prom.h> | |
22 | ||
23 | static struct device_node *cpld_pic_node; | |
bae1d8f1 | 24 | static struct irq_domain *cpld_pic_host; |
1879f711 JR |
25 | |
26 | /* | |
27 | * Bits to ignore in the misc_status register | |
28 | * 0x10 touch screen pendown is hard routed to irq1 | |
29 | * 0x02 pci status is read from pci status register | |
30 | */ | |
31 | #define MISC_IGNORE 0x12 | |
32 | ||
33 | /* | |
34 | * Nothing to ignore in pci status register | |
35 | */ | |
36 | #define PCI_IGNORE 0x00 | |
37 | ||
38 | struct cpld_pic { | |
39 | u8 pci_mask; | |
40 | u8 pci_status; | |
41 | u8 route; | |
42 | u8 misc_mask; | |
43 | u8 misc_status; | |
44 | u8 misc_control; | |
45 | }; | |
46 | ||
47 | static struct cpld_pic __iomem *cpld_regs; | |
48 | ||
49 | static void __iomem * | |
50 | irq_to_pic_mask(unsigned int irq) | |
51 | { | |
52 | return irq <= 7 ? &cpld_regs->pci_mask : &cpld_regs->misc_mask; | |
53 | } | |
54 | ||
55 | static unsigned int | |
56 | irq_to_pic_bit(unsigned int irq) | |
57 | { | |
58 | return 1 << (irq & 0x7); | |
59 | } | |
60 | ||
61 | static void | |
0eb31577 | 62 | cpld_mask_irq(struct irq_data *d) |
1879f711 | 63 | { |
476eb491 | 64 | unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d); |
1879f711 JR |
65 | void __iomem *pic_mask = irq_to_pic_mask(cpld_irq); |
66 | ||
67 | out_8(pic_mask, | |
68 | in_8(pic_mask) | irq_to_pic_bit(cpld_irq)); | |
69 | } | |
70 | ||
71 | static void | |
0eb31577 | 72 | cpld_unmask_irq(struct irq_data *d) |
1879f711 | 73 | { |
476eb491 | 74 | unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d); |
1879f711 JR |
75 | void __iomem *pic_mask = irq_to_pic_mask(cpld_irq); |
76 | ||
77 | out_8(pic_mask, | |
78 | in_8(pic_mask) & ~irq_to_pic_bit(cpld_irq)); | |
79 | } | |
80 | ||
81 | static struct irq_chip cpld_pic = { | |
fc380c0c | 82 | .name = "CPLD PIC", |
0eb31577 LB |
83 | .irq_mask = cpld_mask_irq, |
84 | .irq_ack = cpld_mask_irq, | |
85 | .irq_unmask = cpld_unmask_irq, | |
1879f711 JR |
86 | }; |
87 | ||
88 | static int | |
89 | cpld_pic_get_irq(int offset, u8 ignore, u8 __iomem *statusp, | |
90 | u8 __iomem *maskp) | |
91 | { | |
92 | int cpld_irq; | |
93 | u8 status = in_8(statusp); | |
94 | u8 mask = in_8(maskp); | |
95 | ||
96 | /* ignore don't cares and masked irqs */ | |
97 | status |= (ignore | mask); | |
98 | ||
99 | if (status == 0xff) | |
c42385cd | 100 | return NO_IRQ; |
1879f711 JR |
101 | |
102 | cpld_irq = ffz(status) + offset; | |
103 | ||
104 | return irq_linear_revmap(cpld_pic_host, cpld_irq); | |
105 | } | |
106 | ||
bd0b9ac4 | 107 | static void cpld_pic_cascade(struct irq_desc *desc) |
1879f711 | 108 | { |
5aac2d33 TG |
109 | unsigned int irq; |
110 | ||
1879f711 JR |
111 | irq = cpld_pic_get_irq(0, PCI_IGNORE, &cpld_regs->pci_status, |
112 | &cpld_regs->pci_mask); | |
c42385cd | 113 | if (irq != NO_IRQ) { |
1879f711 JR |
114 | generic_handle_irq(irq); |
115 | return; | |
116 | } | |
117 | ||
118 | irq = cpld_pic_get_irq(8, MISC_IGNORE, &cpld_regs->misc_status, | |
119 | &cpld_regs->misc_mask); | |
c42385cd | 120 | if (irq != NO_IRQ) { |
1879f711 JR |
121 | generic_handle_irq(irq); |
122 | return; | |
123 | } | |
124 | } | |
125 | ||
126 | static int | |
ad3aedfb MZ |
127 | cpld_pic_host_match(struct irq_domain *h, struct device_node *node, |
128 | enum irq_domain_bus_token bus_token) | |
1879f711 JR |
129 | { |
130 | return cpld_pic_node == node; | |
131 | } | |
132 | ||
133 | static int | |
bae1d8f1 | 134 | cpld_pic_host_map(struct irq_domain *h, unsigned int virq, |
1879f711 JR |
135 | irq_hw_number_t hw) |
136 | { | |
98488db9 | 137 | irq_set_status_flags(virq, IRQ_LEVEL); |
ec775d0e | 138 | irq_set_chip_and_handler(virq, &cpld_pic, handle_level_irq); |
1879f711 JR |
139 | return 0; |
140 | } | |
141 | ||
9f70b8eb | 142 | static const struct irq_domain_ops cpld_pic_host_ops = { |
1879f711 JR |
143 | .match = cpld_pic_host_match, |
144 | .map = cpld_pic_host_map, | |
145 | }; | |
146 | ||
147 | void __init | |
148 | mpc5121_ads_cpld_map(void) | |
149 | { | |
150 | struct device_node *np = NULL; | |
151 | ||
152 | np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic"); | |
153 | if (!np) { | |
154 | printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n"); | |
155 | return; | |
156 | } | |
157 | ||
158 | cpld_regs = of_iomap(np, 0); | |
159 | of_node_put(np); | |
160 | } | |
161 | ||
162 | void __init | |
163 | mpc5121_ads_cpld_pic_init(void) | |
164 | { | |
165 | unsigned int cascade_irq; | |
166 | struct device_node *np = NULL; | |
167 | ||
168 | pr_debug("cpld_ic_init\n"); | |
169 | ||
170 | np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic"); | |
171 | if (!np) { | |
172 | printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n"); | |
173 | return; | |
174 | } | |
175 | ||
176 | if (!cpld_regs) | |
177 | goto end; | |
178 | ||
179 | cascade_irq = irq_of_parse_and_map(np, 0); | |
180 | if (cascade_irq == NO_IRQ) | |
181 | goto end; | |
182 | ||
183 | /* | |
184 | * statically route touch screen pendown through 1 | |
185 | * and ignore it here | |
186 | * route all others through our cascade irq | |
187 | */ | |
188 | out_8(&cpld_regs->route, 0xfd); | |
189 | out_8(&cpld_regs->pci_mask, 0xff); | |
190 | /* unmask pci ints in misc mask */ | |
191 | out_8(&cpld_regs->misc_mask, ~(MISC_IGNORE)); | |
192 | ||
193 | cpld_pic_node = of_node_get(np); | |
194 | ||
a8db8cf0 | 195 | cpld_pic_host = irq_domain_add_linear(np, 16, &cpld_pic_host_ops, NULL); |
1879f711 JR |
196 | if (!cpld_pic_host) { |
197 | printk(KERN_ERR "CPLD PIC: failed to allocate irq host!\n"); | |
198 | goto end; | |
199 | } | |
200 | ||
ec775d0e | 201 | irq_set_chained_handler(cascade_irq, cpld_pic_cascade); |
1879f711 JR |
202 | end: |
203 | of_node_put(np); | |
204 | } |