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