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