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 | ||
107 | static void | |
108 | cpld_pic_cascade(unsigned int irq, struct irq_desc *desc) | |
109 | { | |
110 | irq = cpld_pic_get_irq(0, PCI_IGNORE, &cpld_regs->pci_status, | |
111 | &cpld_regs->pci_mask); | |
c42385cd | 112 | if (irq != NO_IRQ) { |
1879f711 JR |
113 | generic_handle_irq(irq); |
114 | return; | |
115 | } | |
116 | ||
117 | irq = cpld_pic_get_irq(8, MISC_IGNORE, &cpld_regs->misc_status, | |
118 | &cpld_regs->misc_mask); | |
c42385cd | 119 | if (irq != NO_IRQ) { |
1879f711 JR |
120 | generic_handle_irq(irq); |
121 | return; | |
122 | } | |
123 | } | |
124 | ||
125 | static int | |
bae1d8f1 | 126 | cpld_pic_host_match(struct irq_domain *h, struct device_node *node) |
1879f711 JR |
127 | { |
128 | return cpld_pic_node == node; | |
129 | } | |
130 | ||
131 | static int | |
bae1d8f1 | 132 | cpld_pic_host_map(struct irq_domain *h, unsigned int virq, |
1879f711 JR |
133 | irq_hw_number_t hw) |
134 | { | |
98488db9 | 135 | irq_set_status_flags(virq, IRQ_LEVEL); |
ec775d0e | 136 | irq_set_chip_and_handler(virq, &cpld_pic, handle_level_irq); |
1879f711 JR |
137 | return 0; |
138 | } | |
139 | ||
9f70b8eb | 140 | static const struct irq_domain_ops cpld_pic_host_ops = { |
1879f711 JR |
141 | .match = cpld_pic_host_match, |
142 | .map = cpld_pic_host_map, | |
143 | }; | |
144 | ||
145 | void __init | |
146 | mpc5121_ads_cpld_map(void) | |
147 | { | |
148 | struct device_node *np = NULL; | |
149 | ||
150 | np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic"); | |
151 | if (!np) { | |
152 | printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n"); | |
153 | return; | |
154 | } | |
155 | ||
156 | cpld_regs = of_iomap(np, 0); | |
157 | of_node_put(np); | |
158 | } | |
159 | ||
160 | void __init | |
161 | mpc5121_ads_cpld_pic_init(void) | |
162 | { | |
163 | unsigned int cascade_irq; | |
164 | struct device_node *np = NULL; | |
165 | ||
166 | pr_debug("cpld_ic_init\n"); | |
167 | ||
168 | np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic"); | |
169 | if (!np) { | |
170 | printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n"); | |
171 | return; | |
172 | } | |
173 | ||
174 | if (!cpld_regs) | |
175 | goto end; | |
176 | ||
177 | cascade_irq = irq_of_parse_and_map(np, 0); | |
178 | if (cascade_irq == NO_IRQ) | |
179 | goto end; | |
180 | ||
181 | /* | |
182 | * statically route touch screen pendown through 1 | |
183 | * and ignore it here | |
184 | * route all others through our cascade irq | |
185 | */ | |
186 | out_8(&cpld_regs->route, 0xfd); | |
187 | out_8(&cpld_regs->pci_mask, 0xff); | |
188 | /* unmask pci ints in misc mask */ | |
189 | out_8(&cpld_regs->misc_mask, ~(MISC_IGNORE)); | |
190 | ||
191 | cpld_pic_node = of_node_get(np); | |
192 | ||
a8db8cf0 | 193 | cpld_pic_host = irq_domain_add_linear(np, 16, &cpld_pic_host_ops, NULL); |
1879f711 JR |
194 | if (!cpld_pic_host) { |
195 | printk(KERN_ERR "CPLD PIC: failed to allocate irq host!\n"); | |
196 | goto end; | |
197 | } | |
198 | ||
ec775d0e | 199 | irq_set_chained_handler(cascade_irq, cpld_pic_cascade); |
1879f711 JR |
200 | end: |
201 | of_node_put(np); | |
202 | } |