xref: /openbmc/u-boot/arch/powerpc/cpu/mpc8xx/interrupts.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
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  
interrupt_init_cpu(unsigned * decrementer_count)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   */
external_interrupt(struct pt_regs * regs)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   */
cpm_interrupt(void * regs)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   */
cpm_error_interrupt(void * dummy)138  static void cpm_error_interrupt(void *dummy)
139  {
140  }
141  
142  /************************************************************************/
143  /*
144   * Install and free an interrupt handler
145   */
irq_install_handler(int vec,interrupt_handler_t * handler,void * arg)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  
irq_free_handler(int vec)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  
cpm_interrupt_init(void)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   */
timer_interrupt_cpu(struct pt_regs * regs)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