1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2000-2002
4  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5  */
6 
7 #include <common.h>
8 #include <mpc8xx.h>
9 #include <mpc8xx_irq.h>
10 #include <asm/cpm_8xx.h>
11 #include <asm/processor.h>
12 #include <asm/io.h>
13 
14 /************************************************************************/
15 
16 /*
17  * CPM interrupt vector functions.
18  */
19 struct interrupt_action {
20 	interrupt_handler_t *handler;
21 	void *arg;
22 };
23 
24 static struct interrupt_action cpm_vecs[CPMVEC_NR];
25 static struct interrupt_action irq_vecs[NR_IRQS];
26 
27 static void cpm_interrupt_init(void);
28 static void cpm_interrupt(void *regs);
29 
30 /************************************************************************/
31 
32 void interrupt_init_cpu(unsigned *decrementer_count)
33 {
34 	immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
35 
36 	*decrementer_count = get_tbclk() / CONFIG_SYS_HZ;
37 
38 	/* disable all interrupts */
39 	out_be32(&immr->im_siu_conf.sc_simask, 0);
40 
41 	/* Configure CPM interrupts */
42 	cpm_interrupt_init();
43 }
44 
45 /************************************************************************/
46 
47 /*
48  * Handle external interrupts
49  */
50 void external_interrupt(struct pt_regs *regs)
51 {
52 	immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
53 	int irq;
54 	ulong simask;
55 	ulong vec, v_bit;
56 
57 	/*
58 	 * read the SIVEC register and shift the bits down
59 	 * to get the irq number
60 	 */
61 	vec = in_be32(&immr->im_siu_conf.sc_sivec);
62 	irq = vec >> 26;
63 	v_bit = 0x80000000UL >> irq;
64 
65 	/*
66 	 * Read Interrupt Mask Register and Mask Interrupts
67 	 */
68 	simask = in_be32(&immr->im_siu_conf.sc_simask);
69 	clrbits_be32(&immr->im_siu_conf.sc_simask, 0xFFFF0000 >> irq);
70 
71 	if (!(irq & 0x1)) {		/* External Interrupt ?     */
72 		ulong siel;
73 
74 		/*
75 		 * Read Interrupt Edge/Level Register
76 		 */
77 		siel = in_be32(&immr->im_siu_conf.sc_siel);
78 
79 		if (siel & v_bit) {	/* edge triggered interrupt ?   */
80 			/*
81 			 * Rewrite SIPEND Register to clear interrupt
82 			 */
83 			out_be32(&immr->im_siu_conf.sc_sipend, v_bit);
84 		}
85 	}
86 
87 	if (irq_vecs[irq].handler != NULL) {
88 		irq_vecs[irq].handler(irq_vecs[irq].arg);
89 	} else {
90 		printf("\nBogus External Interrupt IRQ %d Vector %ld\n",
91 		       irq, vec);
92 		/* turn off the bogus interrupt to avoid it from now */
93 		simask &= ~v_bit;
94 	}
95 	/*
96 	 * Re-Enable old Interrupt Mask
97 	 */
98 	out_be32(&immr->im_siu_conf.sc_simask, simask);
99 }
100 
101 /************************************************************************/
102 
103 /*
104  * CPM interrupt handler
105  */
106 static void cpm_interrupt(void *regs)
107 {
108 	immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
109 	uint vec;
110 
111 	/*
112 	 * Get the vector by setting the ACK bit
113 	 * and then reading the register.
114 	 */
115 	out_be16(&immr->im_cpic.cpic_civr, 1);
116 	vec = in_be16(&immr->im_cpic.cpic_civr);
117 	vec >>= 11;
118 
119 	if (cpm_vecs[vec].handler != NULL) {
120 		(*cpm_vecs[vec].handler) (cpm_vecs[vec].arg);
121 	} else {
122 		clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec);
123 		printf("Masking bogus CPM interrupt vector 0x%x\n", vec);
124 	}
125 	/*
126 	 * After servicing the interrupt,
127 	 * we have to remove the status indicator.
128 	 */
129 	setbits_be32(&immr->im_cpic.cpic_cisr, 1 << vec);
130 }
131 
132 /*
133  * The CPM can generate the error interrupt when there is a race
134  * condition between generating and masking interrupts. All we have
135  * to do is ACK it and return. This is a no-op function so we don't
136  * need any special tests in the interrupt handler.
137  */
138 static void cpm_error_interrupt(void *dummy)
139 {
140 }
141 
142 /************************************************************************/
143 /*
144  * Install and free an interrupt handler
145  */
146 void irq_install_handler(int vec, interrupt_handler_t *handler, void *arg)
147 {
148 	immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
149 
150 	if ((vec & CPMVEC_OFFSET) != 0) {
151 		/* CPM interrupt */
152 		vec &= 0xffff;
153 		if (cpm_vecs[vec].handler != NULL)
154 			printf("CPM interrupt 0x%x replacing 0x%x\n",
155 			       (uint)handler, (uint)cpm_vecs[vec].handler);
156 		cpm_vecs[vec].handler = handler;
157 		cpm_vecs[vec].arg = arg;
158 		setbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec);
159 	} else {
160 		/* SIU interrupt */
161 		if (irq_vecs[vec].handler != NULL)
162 			printf("SIU interrupt %d 0x%x replacing 0x%x\n",
163 			       vec, (uint)handler, (uint)cpm_vecs[vec].handler);
164 		irq_vecs[vec].handler = handler;
165 		irq_vecs[vec].arg = arg;
166 		setbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec));
167 	}
168 }
169 
170 void irq_free_handler(int vec)
171 {
172 	immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
173 
174 	if ((vec & CPMVEC_OFFSET) != 0) {
175 		/* CPM interrupt */
176 		vec &= 0xffff;
177 		clrbits_be32(&immr->im_cpic.cpic_cimr, 1 << vec);
178 		cpm_vecs[vec].handler = NULL;
179 		cpm_vecs[vec].arg = NULL;
180 	} else {
181 		/* SIU interrupt */
182 		clrbits_be32(&immr->im_siu_conf.sc_simask, 1 << (31 - vec));
183 		irq_vecs[vec].handler = NULL;
184 		irq_vecs[vec].arg = NULL;
185 	}
186 }
187 
188 /************************************************************************/
189 
190 static void cpm_interrupt_init(void)
191 {
192 	immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
193 	uint cicr;
194 
195 	/*
196 	 * Initialize the CPM interrupt controller.
197 	 */
198 
199 	cicr = CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1 |
200 	       ((CPM_INTERRUPT / 2) << 13) | CICR_HP_MASK;
201 
202 	out_be32(&immr->im_cpic.cpic_cicr, cicr);
203 	out_be32(&immr->im_cpic.cpic_cimr, 0);
204 
205 	/*
206 	 * Install the error handler.
207 	 */
208 	irq_install_handler(CPMVEC_ERROR, cpm_error_interrupt, NULL);
209 
210 	setbits_be32(&immr->im_cpic.cpic_cicr, CICR_IEN);
211 
212 	/*
213 	 * Install the cpm interrupt handler
214 	 */
215 	irq_install_handler(CPM_INTERRUPT, cpm_interrupt, NULL);
216 }
217 
218 /************************************************************************/
219 
220 /*
221  * timer_interrupt - gets called when the decrementer overflows,
222  * with interrupts disabled.
223  * Trivial implementation - no need to be really accurate.
224  */
225 void timer_interrupt_cpu(struct pt_regs *regs)
226 {
227 	immap_t __iomem *immr = (immap_t __iomem *)CONFIG_SYS_IMMR;
228 
229 	/* Reset Timer Expired and Timers Interrupt Status */
230 	out_be32(&immr->im_clkrstk.cark_plprcrk, KAPWR_KEY);
231 	__asm__ ("nop");
232 	/*
233 	  Clear TEXPS (and TMIST on older chips). SPLSS (on older
234 	  chips) is cleared too.
235 
236 	  Bitwise OR is a read-modify-write operation so ALL bits
237 	  which are cleared by writing `1' would be cleared by
238 	  operations like
239 
240 	  immr->im_clkrst.car_plprcr |= PLPRCR_TEXPS;
241 
242 	  The same can be achieved by simple writing of the PLPRCR
243 	  to itself. If a bit value should be preserved, read the
244 	  register, ZERO the bit and write, not OR, the result back.
245 	*/
246 	setbits_be32(&immr->im_clkrst.car_plprcr, 0);
247 }
248 
249 /************************************************************************/
250