1 /*
2  * (C) Copyright 2007 Michal Simek
3  * (C) Copyright 2004 Atmark Techno, Inc.
4  *
5  * Michal  SIMEK <monstr@monstr.eu>
6  * Yasushi SHOJI <yashi@atmark-techno.com>
7  *
8  * SPDX-License-Identifier:	GPL-2.0+
9  */
10 
11 #include <common.h>
12 #include <command.h>
13 #include <malloc.h>
14 #include <asm/microblaze_intc.h>
15 #include <asm/asm.h>
16 
17 void enable_interrupts(void)
18 {
19 	debug("Enable interrupts for the whole CPU\n");
20 	MSRSET(0x2);
21 }
22 
23 int disable_interrupts(void)
24 {
25 	unsigned int msr;
26 
27 	MFS(msr, rmsr);
28 	MSRCLR(0x2);
29 	return (msr & 0x2) != 0;
30 }
31 
32 static struct irq_action *vecs;
33 static u32 irq_no;
34 
35 /* mapping structure to interrupt controller */
36 microblaze_intc_t *intc;
37 
38 /* default handler */
39 static void def_hdlr(void)
40 {
41 	puts("def_hdlr\n");
42 }
43 
44 static void enable_one_interrupt(int irq)
45 {
46 	int mask;
47 	int offset = 1;
48 
49 	offset <<= irq;
50 	mask = intc->ier;
51 	intc->ier = (mask | offset);
52 
53 	debug("Enable one interrupt irq %x - mask %x,ier %x\n", offset, mask,
54 	      intc->ier);
55 	debug("INTC isr %x, ier %x, iar %x, mer %x\n", intc->isr, intc->ier,
56 	      intc->iar, intc->mer);
57 }
58 
59 static void disable_one_interrupt(int irq)
60 {
61 	int mask;
62 	int offset = 1;
63 
64 	offset <<= irq;
65 	mask = intc->ier;
66 	intc->ier = (mask & ~offset);
67 
68 	debug("Disable one interrupt irq %x - mask %x,ier %x\n", irq, mask,
69 	      intc->ier);
70 	debug("INTC isr %x, ier %x, iar %x, mer %x\n", intc->isr, intc->ier,
71 	      intc->iar, intc->mer);
72 }
73 
74 int install_interrupt_handler(int irq, interrupt_handler_t *hdlr, void *arg)
75 {
76 	struct irq_action *act;
77 
78 	/* irq out of range */
79 	if ((irq < 0) || (irq > irq_no)) {
80 		puts("IRQ out of range\n");
81 		return -1;
82 	}
83 	act = &vecs[irq];
84 	if (hdlr) {		/* enable */
85 		act->handler = hdlr;
86 		act->arg = arg;
87 		act->count = 0;
88 		enable_one_interrupt(irq);
89 		return 0;
90 	}
91 
92 	/* Disable */
93 	act->handler = (interrupt_handler_t *)def_hdlr;
94 	act->arg = (void *)irq;
95 	disable_one_interrupt(irq);
96 	return 1;
97 }
98 
99 /* initialization interrupt controller - hardware */
100 static void intc_init(void)
101 {
102 	intc->mer = 0;
103 	intc->ier = 0;
104 	intc->iar = 0xFFFFFFFF;
105 	/* XIntc_Start - hw_interrupt enable and all interrupt enable */
106 	intc->mer = 0x3;
107 
108 	debug("INTC isr %x, ier %x, iar %x, mer %x\n", intc->isr, intc->ier,
109 	      intc->iar, intc->mer);
110 }
111 
112 int interrupt_init(void)
113 {
114 	int i;
115 
116 #if defined(CONFIG_SYS_INTC_0_ADDR) && defined(CONFIG_SYS_INTC_0_NUM)
117 	intc = (microblaze_intc_t *)CONFIG_SYS_INTC_0_ADDR;
118 	irq_no = CONFIG_SYS_INTC_0_NUM;
119 #endif
120 	if (irq_no) {
121 		vecs = calloc(1, sizeof(struct irq_action) * irq_no);
122 		if (vecs == NULL) {
123 			puts("Interrupt vector allocation failed\n");
124 			return -1;
125 		}
126 
127 		/* initialize irq list */
128 		for (i = 0; i < irq_no; i++) {
129 			vecs[i].handler = (interrupt_handler_t *)def_hdlr;
130 			vecs[i].arg = (void *)i;
131 			vecs[i].count = 0;
132 		}
133 		/* initialize intc controller */
134 		intc_init();
135 		enable_interrupts();
136 	} else {
137 		puts("Undefined interrupt controller\n");
138 	}
139 	return 0;
140 }
141 
142 void interrupt_handler(void)
143 {
144 	int irqs = intc->ivr;	/* find active interrupt */
145 	int mask = 1;
146 	int value;
147 	struct irq_action *act = vecs + irqs;
148 
149 	debug("INTC isr %x, ier %x, iar %x, mer %x\n", intc->isr, intc->ier,
150 	      intc->iar, intc->mer);
151 #ifdef DEBUG
152 	R14(value);
153 #endif
154 	debug("Interrupt handler on %x line, r14 %x\n", irqs, value);
155 
156 	debug("Jumping to interrupt handler rutine addr %x,count %x,arg %x\n",
157 	      (u32)act->handler, act->count, (u32)act->arg);
158 	act->handler(act->arg);
159 	act->count++;
160 
161 	intc->iar = mask << irqs;
162 
163 	debug("Dump INTC reg, isr %x, ier %x, iar %x, mer %x\n", intc->isr,
164 	      intc->ier, intc->iar, intc->mer);
165 #ifdef DEBUG
166 	R14(value);
167 #endif
168 	debug("Interrupt handler on %x line, r14 %x\n", irqs, value);
169 }
170 
171 #if defined(CONFIG_CMD_IRQ)
172 int do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, const char *argv[])
173 {
174 	int i;
175 	struct irq_action *act = vecs;
176 
177 	if (irq_no) {
178 		puts("\nInterrupt-Information:\n\n"
179 		      "Nr  Routine   Arg       Count\n"
180 		      "-----------------------------\n");
181 
182 		for (i = 0; i < irq_no; i++) {
183 			if (act->handler != (interrupt_handler_t *)def_hdlr) {
184 				printf("%02d  %08x  %08x  %d\n", i,
185 				       (int)act->handler, (int)act->arg,
186 				       act->count);
187 			}
188 			act++;
189 		}
190 		puts("\n");
191 	} else {
192 		puts("Undefined interrupt controller\n");
193 	}
194 	return 0;
195 }
196 #endif
197