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