1acf9e575SChristophe Leroy // SPDX-License-Identifier: GPL-2.0
2acf9e575SChristophe Leroy /*
3acf9e575SChristophe Leroy  * Interrupt controller for the
4acf9e575SChristophe Leroy  * Communication Processor Module.
5acf9e575SChristophe Leroy  * Copyright (c) 1997 Dan error_act (dmalek@jlc.net)
6acf9e575SChristophe Leroy  */
7acf9e575SChristophe Leroy #include <linux/kernel.h>
8acf9e575SChristophe Leroy #include <linux/interrupt.h>
9acf9e575SChristophe Leroy #include <linux/irqdomain.h>
10acf9e575SChristophe Leroy #include <linux/of_irq.h>
11*22add2a2SChristophe Leroy #include <linux/platform_device.h>
12acf9e575SChristophe Leroy #include <asm/cpm1.h>
13acf9e575SChristophe Leroy 
14acf9e575SChristophe Leroy static cpic8xx_t __iomem *cpic_reg;
15acf9e575SChristophe Leroy 
16acf9e575SChristophe Leroy static struct irq_domain *cpm_pic_host;
17acf9e575SChristophe Leroy 
18acf9e575SChristophe Leroy static void cpm_mask_irq(struct irq_data *d)
19acf9e575SChristophe Leroy {
20acf9e575SChristophe Leroy 	unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
21acf9e575SChristophe Leroy 
22acf9e575SChristophe Leroy 	clrbits32(&cpic_reg->cpic_cimr, (1 << cpm_vec));
23acf9e575SChristophe Leroy }
24acf9e575SChristophe Leroy 
25acf9e575SChristophe Leroy static void cpm_unmask_irq(struct irq_data *d)
26acf9e575SChristophe Leroy {
27acf9e575SChristophe Leroy 	unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
28acf9e575SChristophe Leroy 
29acf9e575SChristophe Leroy 	setbits32(&cpic_reg->cpic_cimr, (1 << cpm_vec));
30acf9e575SChristophe Leroy }
31acf9e575SChristophe Leroy 
32acf9e575SChristophe Leroy static void cpm_end_irq(struct irq_data *d)
33acf9e575SChristophe Leroy {
34acf9e575SChristophe Leroy 	unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
35acf9e575SChristophe Leroy 
36acf9e575SChristophe Leroy 	out_be32(&cpic_reg->cpic_cisr, (1 << cpm_vec));
37acf9e575SChristophe Leroy }
38acf9e575SChristophe Leroy 
39acf9e575SChristophe Leroy static struct irq_chip cpm_pic = {
40acf9e575SChristophe Leroy 	.name = "CPM PIC",
41acf9e575SChristophe Leroy 	.irq_mask = cpm_mask_irq,
42acf9e575SChristophe Leroy 	.irq_unmask = cpm_unmask_irq,
43acf9e575SChristophe Leroy 	.irq_eoi = cpm_end_irq,
44acf9e575SChristophe Leroy };
45acf9e575SChristophe Leroy 
46acf9e575SChristophe Leroy int cpm_get_irq(void)
47acf9e575SChristophe Leroy {
48acf9e575SChristophe Leroy 	int cpm_vec;
49acf9e575SChristophe Leroy 
50acf9e575SChristophe Leroy 	/*
51acf9e575SChristophe Leroy 	 * Get the vector by setting the ACK bit and then reading
52acf9e575SChristophe Leroy 	 * the register.
53acf9e575SChristophe Leroy 	 */
54acf9e575SChristophe Leroy 	out_be16(&cpic_reg->cpic_civr, 1);
55acf9e575SChristophe Leroy 	cpm_vec = in_be16(&cpic_reg->cpic_civr);
56acf9e575SChristophe Leroy 	cpm_vec >>= 11;
57acf9e575SChristophe Leroy 
58acf9e575SChristophe Leroy 	return irq_linear_revmap(cpm_pic_host, cpm_vec);
59acf9e575SChristophe Leroy }
60acf9e575SChristophe Leroy 
61acf9e575SChristophe Leroy static int cpm_pic_host_map(struct irq_domain *h, unsigned int virq,
62acf9e575SChristophe Leroy 			    irq_hw_number_t hw)
63acf9e575SChristophe Leroy {
64acf9e575SChristophe Leroy 	pr_debug("cpm_pic_host_map(%d, 0x%lx)\n", virq, hw);
65acf9e575SChristophe Leroy 
66acf9e575SChristophe Leroy 	irq_set_status_flags(virq, IRQ_LEVEL);
67acf9e575SChristophe Leroy 	irq_set_chip_and_handler(virq, &cpm_pic, handle_fasteoi_irq);
68acf9e575SChristophe Leroy 	return 0;
69acf9e575SChristophe Leroy }
70acf9e575SChristophe Leroy 
71acf9e575SChristophe Leroy static const struct irq_domain_ops cpm_pic_host_ops = {
72acf9e575SChristophe Leroy 	.map = cpm_pic_host_map,
73acf9e575SChristophe Leroy };
74acf9e575SChristophe Leroy 
75acf9e575SChristophe Leroy unsigned int __init cpm_pic_init(void)
76acf9e575SChristophe Leroy {
77acf9e575SChristophe Leroy 	struct device_node *np = NULL;
78acf9e575SChristophe Leroy 	struct resource res;
79*22add2a2SChristophe Leroy 	unsigned int sirq = 0, hwirq;
80acf9e575SChristophe Leroy 	int ret;
81acf9e575SChristophe Leroy 
82acf9e575SChristophe Leroy 	pr_debug("cpm_pic_init\n");
83acf9e575SChristophe Leroy 
84acf9e575SChristophe Leroy 	np = of_find_compatible_node(NULL, NULL, "fsl,cpm1-pic");
85acf9e575SChristophe Leroy 	if (np == NULL)
86acf9e575SChristophe Leroy 		np = of_find_compatible_node(NULL, "cpm-pic", "CPM");
87acf9e575SChristophe Leroy 	if (np == NULL) {
88acf9e575SChristophe Leroy 		printk(KERN_ERR "CPM PIC init: can not find cpm-pic node\n");
89acf9e575SChristophe Leroy 		return sirq;
90acf9e575SChristophe Leroy 	}
91acf9e575SChristophe Leroy 
92acf9e575SChristophe Leroy 	ret = of_address_to_resource(np, 0, &res);
93acf9e575SChristophe Leroy 	if (ret)
94acf9e575SChristophe Leroy 		goto end;
95acf9e575SChristophe Leroy 
96acf9e575SChristophe Leroy 	cpic_reg = ioremap(res.start, resource_size(&res));
97acf9e575SChristophe Leroy 	if (cpic_reg == NULL)
98acf9e575SChristophe Leroy 		goto end;
99acf9e575SChristophe Leroy 
100acf9e575SChristophe Leroy 	sirq = irq_of_parse_and_map(np, 0);
101acf9e575SChristophe Leroy 	if (!sirq)
102acf9e575SChristophe Leroy 		goto end;
103acf9e575SChristophe Leroy 
104acf9e575SChristophe Leroy 	/* Initialize the CPM interrupt controller. */
105acf9e575SChristophe Leroy 	hwirq = (unsigned int)virq_to_hw(sirq);
106acf9e575SChristophe Leroy 	out_be32(&cpic_reg->cpic_cicr,
107acf9e575SChristophe Leroy 	    (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
108acf9e575SChristophe Leroy 		((hwirq/2) << 13) | CICR_HP_MASK);
109acf9e575SChristophe Leroy 
110acf9e575SChristophe Leroy 	out_be32(&cpic_reg->cpic_cimr, 0);
111acf9e575SChristophe Leroy 
112acf9e575SChristophe Leroy 	cpm_pic_host = irq_domain_add_linear(np, 64, &cpm_pic_host_ops, NULL);
113acf9e575SChristophe Leroy 	if (cpm_pic_host == NULL) {
114acf9e575SChristophe Leroy 		printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
115acf9e575SChristophe Leroy 		sirq = 0;
116acf9e575SChristophe Leroy 		goto end;
117acf9e575SChristophe Leroy 	}
118acf9e575SChristophe Leroy 
119acf9e575SChristophe Leroy 	setbits32(&cpic_reg->cpic_cicr, CICR_IEN);
120acf9e575SChristophe Leroy 
121acf9e575SChristophe Leroy end:
122acf9e575SChristophe Leroy 	of_node_put(np);
123acf9e575SChristophe Leroy 	return sirq;
124acf9e575SChristophe Leroy }
125*22add2a2SChristophe Leroy 
126*22add2a2SChristophe Leroy /*
127*22add2a2SChristophe Leroy  * The CPM can generate the error interrupt when there is a race condition
128*22add2a2SChristophe Leroy  * between generating and masking interrupts.  All we have to do is ACK it
129*22add2a2SChristophe Leroy  * and return.  This is a no-op function so we don't need any special
130*22add2a2SChristophe Leroy  * tests in the interrupt handler.
131*22add2a2SChristophe Leroy  */
132*22add2a2SChristophe Leroy static irqreturn_t cpm_error_interrupt(int irq, void *dev)
133*22add2a2SChristophe Leroy {
134*22add2a2SChristophe Leroy 	return IRQ_HANDLED;
135*22add2a2SChristophe Leroy }
136*22add2a2SChristophe Leroy 
137*22add2a2SChristophe Leroy static int cpm_error_probe(struct platform_device *pdev)
138*22add2a2SChristophe Leroy {
139*22add2a2SChristophe Leroy 	int irq;
140*22add2a2SChristophe Leroy 
141*22add2a2SChristophe Leroy 	irq = platform_get_irq(pdev, 0);
142*22add2a2SChristophe Leroy 	if (irq < 0)
143*22add2a2SChristophe Leroy 		return irq;
144*22add2a2SChristophe Leroy 
145*22add2a2SChristophe Leroy 	return request_irq(irq, cpm_error_interrupt, IRQF_NO_THREAD, "error", NULL);
146*22add2a2SChristophe Leroy }
147*22add2a2SChristophe Leroy 
148*22add2a2SChristophe Leroy static const struct of_device_id cpm_error_ids[] = {
149*22add2a2SChristophe Leroy 	{ .compatible = "fsl,cpm1" },
150*22add2a2SChristophe Leroy 	{ .type = "cpm" },
151*22add2a2SChristophe Leroy 	{},
152*22add2a2SChristophe Leroy };
153*22add2a2SChristophe Leroy 
154*22add2a2SChristophe Leroy static struct platform_driver cpm_error_driver = {
155*22add2a2SChristophe Leroy 	.driver	= {
156*22add2a2SChristophe Leroy 		.name		= "cpm-error",
157*22add2a2SChristophe Leroy 		.of_match_table	= cpm_error_ids,
158*22add2a2SChristophe Leroy 	},
159*22add2a2SChristophe Leroy 	.probe	= cpm_error_probe,
160*22add2a2SChristophe Leroy };
161*22add2a2SChristophe Leroy 
162*22add2a2SChristophe Leroy static int __init cpm_error_init(void)
163*22add2a2SChristophe Leroy {
164*22add2a2SChristophe Leroy 	return platform_driver_register(&cpm_error_driver);
165*22add2a2SChristophe Leroy }
166*22add2a2SChristophe Leroy subsys_initcall(cpm_error_init);
167