hw_opic.c revision 1.1 1 1.1 christos /* This file is part of the program psim.
2 1.1 christos
3 1.1 christos Copyright (C) 1994-1996, Andrew Cagney <cagney (at) highland.com.au>
4 1.1 christos
5 1.1 christos This program is free software; you can redistribute it and/or modify
6 1.1 christos it under the terms of the GNU General Public License as published by
7 1.1 christos the Free Software Foundation; either version 2 of the License, or
8 1.1 christos (at your option) any later version.
9 1.1 christos
10 1.1 christos This program is distributed in the hope that it will be useful,
11 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
12 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 1.1 christos GNU General Public License for more details.
14 1.1 christos
15 1.1 christos You should have received a copy of the GNU General Public License
16 1.1 christos along with this program; if not, write to the Free Software
17 1.1 christos Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 1.1 christos
19 1.1 christos */
20 1.1 christos
21 1.1 christos
22 1.1 christos #ifndef _HW_OPIC_C_
23 1.1 christos #define _HW_OPIC_C_
24 1.1 christos
25 1.1 christos #include "device_table.h"
26 1.1 christos
27 1.1 christos #ifdef HAVE_STRING_H
28 1.1 christos #include <string.h>
29 1.1 christos #else
30 1.1 christos #ifdef HAVE_STRINGS_H
31 1.1 christos #include <strings.h>
32 1.1 christos #endif
33 1.1 christos #endif
34 1.1 christos
35 1.1 christos
36 1.1 christos /* DEVICE
37 1.1 christos
38 1.1 christos
39 1.1 christos opic - Open Programmable Interrupt Controller (OpenPIC)
40 1.1 christos
41 1.1 christos
42 1.1 christos DESCRIPTION
43 1.1 christos
44 1.1 christos
45 1.1 christos This device implements the core of the OpenPIC interrupt controller
46 1.1 christos as described in the OpenPIC specification 1.2 and other related
47 1.1 christos documents.
48 1.1 christos
49 1.1 christos The model includes:
50 1.1 christos
51 1.1 christos o Up to 2048 external interrupt sources
52 1.1 christos
53 1.1 christos o The four count down timers
54 1.1 christos
55 1.1 christos o The four interprocessor multicast interrupts
56 1.1 christos
57 1.1 christos o multiprocessor support
58 1.1 christos
59 1.1 christos o Full tracing to assist help debugging
60 1.1 christos
61 1.1 christos o Support for all variations of edge/level x high/low polarity.
62 1.1 christos
63 1.1 christos
64 1.1 christos
65 1.1 christos PROPERTIES
66 1.1 christos
67 1.1 christos
68 1.1 christos reg = <address> <size> ... (required)
69 1.1 christos
70 1.1 christos Determine where the device lives in the parents address space. The
71 1.1 christos first <<address>> <<size>> pair specifies the address of the
72 1.1 christos interrupt destination unit (which might contain an interrupt source
73 1.1 christos unit) while successive reg entries specify additional interrupt
74 1.1 christos source units.
75 1.1 christos
76 1.1 christos Note that for an <<opic>> device attached to a <<pci>> bus, the
77 1.1 christos first <<reg>> entry may need to be ignored it will be the address
78 1.1 christos of the devices configuration registers.
79 1.1 christos
80 1.1 christos
81 1.1 christos interrupt-ranges = <int-number> <range> ... (required)
82 1.1 christos
83 1.1 christos A list of pairs. Each pair corresponds to a block of interrupt
84 1.1 christos source units (the address of which being specified by the
85 1.1 christos corresponding reg tupple). <<int-number>> is the number of the
86 1.1 christos first interrupt in the block while <<range>> is the number of
87 1.1 christos interrupts in the block.
88 1.1 christos
89 1.1 christos
90 1.1 christos timer-frequency = <integer> (optional)
91 1.1 christos
92 1.1 christos If present, specifies the default value of the timer frequency
93 1.1 christos reporting register. By default a value of 1 HZ is used. The value
94 1.1 christos is arbitrary, the timers are always updated once per machine cycle.
95 1.1 christos
96 1.1 christos
97 1.1 christos vendor-identification = <integer> (optional)
98 1.1 christos
99 1.1 christos If present, specifies the value to be returned when the vendor
100 1.1 christos identification register is read.
101 1.1 christos
102 1.1 christos
103 1.1 christos EXAMPLES
104 1.1 christos
105 1.1 christos
106 1.1 christos See the test suite directory:
107 1.1 christos
108 1.1 christos | psim-test/hw-opic
109 1.1 christos
110 1.1 christos
111 1.1 christos BUGS
112 1.1 christos
113 1.1 christos For an OPIC controller attached to a PCI bus, it is not clear what
114 1.1 christos the value of the <<reg>> and <<interrupt-ranges>> properties should
115 1.1 christos be. In particular, the PCI firmware bindings require the first
116 1.1 christos value of the <<reg>> property to specify the devices configuration
117 1.1 christos address while the OpenPIC bindings require that same entry to
118 1.1 christos specify the address of the Interrupt Delivery Unit. This
119 1.1 christos implementation checks for and, if present, ignores any
120 1.1 christos configuration address (and its corresponding <<interrupt-ranges>>
121 1.1 christos entry).
122 1.1 christos
123 1.1 christos The OpenPIC specification requires the controller to be fair when
124 1.1 christos distributing interrupts between processors. At present the
125 1.1 christos algorithm used isn't fair. It is biased towards processor zero.
126 1.1 christos
127 1.1 christos The OpenPIC specification includes a 8259 pass through mode. This
128 1.1 christos is not supported.
129 1.1 christos
130 1.1 christos
131 1.1 christos REFERENCES
132 1.1 christos
133 1.1 christos
134 1.1 christos PowerPC Multiprocessor Interrupt Controller (MPIC), January 19,
135 1.1 christos 1996. Available from IBM.
136 1.1 christos
137 1.1 christos
138 1.1 christos The Open Programmable Interrupt Controller (PIC) Register Interface
139 1.1 christos Specification Revision 1.2. Issue Date: Opctober 1995. Available
140 1.1 christos somewhere on AMD's web page (http://www.amd.com/)
141 1.1 christos
142 1.1 christos
143 1.1 christos PowerPC Microprocessor Common Hardware Reference Platform (CHRP)
144 1.1 christos System bindings to: IEEE Std 1275-1994 Standard for Boot
145 1.1 christos (Initialization, Configuration) Firmware. Revision 1.2b (INTERIM
146 1.1 christos DRAFT). April 22, 1996. Available on the Open Firmware web site
147 1.1 christos http://playground.sun.com/p1275/.
148 1.1 christos
149 1.1 christos
150 1.1 christos */
151 1.1 christos
152 1.1 christos
153 1.1 christos /* forward types */
154 1.1 christos
155 1.1 christos typedef struct _hw_opic_device hw_opic_device;
156 1.1 christos
157 1.1 christos
158 1.1 christos /* bounds */
159 1.1 christos
160 1.1 christos enum {
161 1.1 christos max_nr_interrupt_sources = 2048,
162 1.1 christos max_nr_interrupt_destinations = 32,
163 1.1 christos max_nr_task_priorities = 16,
164 1.1 christos };
165 1.1 christos
166 1.1 christos
167 1.1 christos enum {
168 1.1 christos opic_alignment = 16,
169 1.1 christos };
170 1.1 christos
171 1.1 christos
172 1.1 christos /* global configuration register */
173 1.1 christos
174 1.1 christos enum {
175 1.1 christos gcr0_8259_bit = 0x20000000,
176 1.1 christos gcr0_reset_bit = 0x80000000,
177 1.1 christos };
178 1.1 christos
179 1.1 christos
180 1.1 christos /* offsets and sizes */
181 1.1 christos
182 1.1 christos enum {
183 1.1 christos idu_isu_base = 0x10000,
184 1.1 christos sizeof_isu_register_block = 32,
185 1.1 christos idu_per_processor_register_base = 0x20000,
186 1.1 christos sizeof_idu_per_processor_register_block = 0x1000,
187 1.1 christos idu_timer_base = 0x01100,
188 1.1 christos sizeof_timer_register_block = 0x00040,
189 1.1 christos };
190 1.1 christos
191 1.1 christos
192 1.1 christos /* Interrupt sources */
193 1.1 christos
194 1.1 christos enum {
195 1.1 christos isu_mask_bit = 0x80000000,
196 1.1 christos isu_active_bit = 0x40000000,
197 1.1 christos isu_multicast_bit = 0x20000000,
198 1.1 christos isu_positive_polarity_bit = 0x00800000,
199 1.1 christos isu_level_triggered_bit = 0x00400000,
200 1.1 christos isu_priority_shift = 16,
201 1.1 christos isu_vector_bits = 0x000000ff,
202 1.1 christos };
203 1.1 christos
204 1.1 christos
205 1.1 christos typedef struct _opic_interrupt_source {
206 1.1 christos unsigned is_masked; /* left in place */
207 1.1 christos unsigned is_multicast; /* left in place */
208 1.1 christos unsigned is_positive_polarity; /* left in place */
209 1.1 christos unsigned is_level_triggered; /* left in place */
210 1.1 christos unsigned priority;
211 1.1 christos unsigned vector;
212 1.1 christos /* misc */
213 1.1 christos int nr;
214 1.1 christos unsigned destination;
215 1.1 christos unsigned pending;
216 1.1 christos unsigned in_service;
217 1.1 christos } opic_interrupt_source;
218 1.1 christos
219 1.1 christos
220 1.1 christos /* interrupt destinations (normally processors) */
221 1.1 christos
222 1.1 christos typedef struct _opic_interrupt_destination {
223 1.1 christos int nr;
224 1.1 christos unsigned base_priority;
225 1.1 christos opic_interrupt_source *current_pending;
226 1.1 christos opic_interrupt_source *current_in_service;
227 1.1 christos unsigned bit;
228 1.1 christos int init_port;
229 1.1 christos int intr_port;
230 1.1 christos } opic_interrupt_destination;
231 1.1 christos
232 1.1 christos
233 1.1 christos /* address map descriptors */
234 1.1 christos
235 1.1 christos typedef struct _opic_isu_block { /* interrupt source unit block */
236 1.1 christos int space;
237 1.1 christos unsigned_word address;
238 1.1 christos unsigned size;
239 1.1 christos unsigned_cell int_number;
240 1.1 christos unsigned_cell range;
241 1.1 christos int reg;
242 1.1 christos } opic_isu_block;
243 1.1 christos
244 1.1 christos
245 1.1 christos typedef struct _opic_idu { /* interrupt delivery unit */
246 1.1 christos int reg;
247 1.1 christos int space;
248 1.1 christos unsigned_word address;
249 1.1 christos unsigned size;
250 1.1 christos } opic_idu;
251 1.1 christos
252 1.1 christos typedef enum {
253 1.1 christos /* bad */
254 1.1 christos invalid_opic_register,
255 1.1 christos /* interrupt source */
256 1.1 christos interrupt_source_N_destination_register,
257 1.1 christos interrupt_source_N_vector_priority_register,
258 1.1 christos /* timers */
259 1.1 christos timer_N_destination_register,
260 1.1 christos timer_N_vector_priority_register,
261 1.1 christos timer_N_base_count_register,
262 1.1 christos timer_N_current_count_register,
263 1.1 christos timer_frequency_reporting_register,
264 1.1 christos /* inter-processor interrupts */
265 1.1 christos ipi_N_vector_priority_register,
266 1.1 christos ipi_N_dispatch_register,
267 1.1 christos /* global configuration */
268 1.1 christos spurious_vector_register,
269 1.1 christos processor_init_register,
270 1.1 christos vendor_identification_register,
271 1.1 christos global_configuration_register_N,
272 1.1 christos feature_reporting_register_N,
273 1.1 christos /* per processor */
274 1.1 christos end_of_interrupt_register_N,
275 1.1 christos interrupt_acknowledge_register_N,
276 1.1 christos current_task_priority_register_N,
277 1.1 christos } opic_register;
278 1.1 christos
279 1.1 christos static const char *
280 1.1 christos opic_register_name(opic_register type)
281 1.1 christos {
282 1.1 christos switch (type) {
283 1.1 christos case invalid_opic_register: return "invalid_opic_register";
284 1.1 christos case interrupt_source_N_destination_register: return "interrupt_source_N_destination_register";
285 1.1 christos case interrupt_source_N_vector_priority_register: return "interrupt_source_N_vector_priority_register";
286 1.1 christos case timer_N_destination_register: return "timer_N_destination_register";
287 1.1 christos case timer_N_vector_priority_register: return "timer_N_vector_priority_register";
288 1.1 christos case timer_N_base_count_register: return "timer_N_base_count_register";
289 1.1 christos case timer_N_current_count_register: return "timer_N_current_count_register";
290 1.1 christos case timer_frequency_reporting_register: return "timer_frequency_reporting_register";
291 1.1 christos case ipi_N_vector_priority_register: return "ipi_N_vector_priority_register";
292 1.1 christos case ipi_N_dispatch_register: return "ipi_N_dispatch_register";
293 1.1 christos case spurious_vector_register: return "spurious_vector_register";
294 1.1 christos case processor_init_register: return "processor_init_register";
295 1.1 christos case vendor_identification_register: return "vendor_identification_register";
296 1.1 christos case global_configuration_register_N: return "global_configuration_register_N";
297 1.1 christos case feature_reporting_register_N: return "feature_reporting_register_N";
298 1.1 christos case end_of_interrupt_register_N: return "end_of_interrupt_register_N";
299 1.1 christos case interrupt_acknowledge_register_N: return "interrupt_acknowledge_register_N";
300 1.1 christos case current_task_priority_register_N: return "current_task_priority_register_N";
301 1.1 christos }
302 1.1 christos return NULL;
303 1.1 christos }
304 1.1 christos
305 1.1 christos
306 1.1 christos
307 1.1 christos /* timers */
308 1.1 christos
309 1.1 christos typedef struct _opic_timer {
310 1.1 christos int nr;
311 1.1 christos device *me; /* find my way home */
312 1.1 christos hw_opic_device *opic; /* ditto */
313 1.1 christos unsigned base_count;
314 1.1 christos int inhibited;
315 1.1 christos signed64 count; /* *ONLY* if inhibited */
316 1.1 christos event_entry_tag timeout_event;
317 1.1 christos opic_interrupt_source *interrupt_source;
318 1.1 christos } opic_timer;
319 1.1 christos
320 1.1 christos
321 1.1 christos /* the OPIC */
322 1.1 christos
323 1.1 christos struct _hw_opic_device {
324 1.1 christos
325 1.1 christos /* vendor id */
326 1.1 christos unsigned vendor_identification;
327 1.1 christos
328 1.1 christos /* interrupt destinations - processors */
329 1.1 christos int nr_interrupt_destinations;
330 1.1 christos opic_interrupt_destination *interrupt_destination;
331 1.1 christos unsigned sizeof_interrupt_destination;
332 1.1 christos
333 1.1 christos /* bogus interrupts */
334 1.1 christos int spurious_vector;
335 1.1 christos
336 1.1 christos /* interrupt sources - external interrupt source units + extra internal ones */
337 1.1 christos int nr_interrupt_sources;
338 1.1 christos opic_interrupt_source *interrupt_source;
339 1.1 christos unsigned sizeof_interrupt_source;
340 1.1 christos
341 1.1 christos /* external interrupts */
342 1.1 christos int nr_external_interrupts;
343 1.1 christos opic_interrupt_source *external_interrupt_source;
344 1.1 christos
345 1.1 christos /* inter-processor-interrupts */
346 1.1 christos int nr_interprocessor_interrupts;
347 1.1 christos opic_interrupt_source *interprocessor_interrupt_source;
348 1.1 christos
349 1.1 christos /* timers */
350 1.1 christos int nr_timer_interrupts;
351 1.1 christos opic_timer *timer;
352 1.1 christos unsigned sizeof_timer;
353 1.1 christos opic_interrupt_source *timer_interrupt_source;
354 1.1 christos unsigned timer_frequency;
355 1.1 christos
356 1.1 christos /* init register */
357 1.1 christos unsigned32 init;
358 1.1 christos
359 1.1 christos /* address maps */
360 1.1 christos opic_idu idu;
361 1.1 christos int nr_isu_blocks;
362 1.1 christos opic_isu_block *isu_block;
363 1.1 christos };
364 1.1 christos
365 1.1 christos
366 1.1 christos static void
367 1.1 christos hw_opic_init_data(device *me)
368 1.1 christos {
369 1.1 christos hw_opic_device *opic = (hw_opic_device*)device_data(me);
370 1.1 christos int isb;
371 1.1 christos int idu_reg;
372 1.1 christos int nr_isu_blocks;
373 1.1 christos int i;
374 1.1 christos
375 1.1 christos /* determine the first valid reg property entry (there could be
376 1.1 christos leading reg entries with invalid (zero) size fields) and the
377 1.1 christos number of isu entries found in the reg property. */
378 1.1 christos idu_reg = 0;
379 1.1 christos nr_isu_blocks = 0;
380 1.1 christos while (1) {
381 1.1 christos reg_property_spec unit;
382 1.1 christos int attach_space;
383 1.1 christos unsigned_word attach_address;
384 1.1 christos unsigned attach_size;
385 1.1 christos if (!device_find_reg_array_property(me, "reg", idu_reg + nr_isu_blocks,
386 1.1 christos &unit))
387 1.1 christos break;
388 1.1 christos if (nr_isu_blocks > 0
389 1.1 christos || (device_address_to_attach_address(device_parent(me), &unit.address,
390 1.1 christos &attach_space, &attach_address,
391 1.1 christos me)
392 1.1 christos && device_size_to_attach_size(device_parent(me), &unit.size,
393 1.1 christos &attach_size,
394 1.1 christos me))) {
395 1.1 christos /* we count any thing once we've found one valid address/size pair */
396 1.1 christos nr_isu_blocks += 1;
397 1.1 christos }
398 1.1 christos else {
399 1.1 christos idu_reg += 1;
400 1.1 christos }
401 1.1 christos }
402 1.1 christos
403 1.1 christos /* determine the number and location of the multiple interrupt
404 1.1 christos source units and the single interrupt delivery unit */
405 1.1 christos if (opic->isu_block == NULL) {
406 1.1 christos int reg_nr;
407 1.1 christos opic->nr_isu_blocks = nr_isu_blocks;
408 1.1 christos opic->isu_block = zalloc(sizeof(opic_isu_block) * opic->nr_isu_blocks);
409 1.1 christos isb = 0;
410 1.1 christos reg_nr = idu_reg;
411 1.1 christos while (isb < opic->nr_isu_blocks) {
412 1.1 christos reg_property_spec reg;
413 1.1 christos if (!device_find_reg_array_property(me, "reg", reg_nr, ®))
414 1.1 christos device_error(me, "reg property missing entry number %d", reg_nr);
415 1.1 christos opic->isu_block[isb].reg = reg_nr;
416 1.1 christos if (!device_address_to_attach_address(device_parent(me), ®.address,
417 1.1 christos &opic->isu_block[isb].space,
418 1.1 christos &opic->isu_block[isb].address,
419 1.1 christos me)
420 1.1 christos || !device_size_to_attach_size(device_parent(me), ®.size,
421 1.1 christos &opic->isu_block[isb].size,
422 1.1 christos me)) {
423 1.1 christos device_error(me, "reg property entry %d invalid", reg_nr);
424 1.1 christos }
425 1.1 christos if (!device_find_integer_array_property(me, "interrupt-ranges",
426 1.1 christos reg_nr * 2,
427 1.1 christos &opic->isu_block[isb].int_number)
428 1.1 christos || !device_find_integer_array_property(me, "interrupt-ranges",
429 1.1 christos reg_nr * 2 + 1,
430 1.1 christos &opic->isu_block[isb].range))
431 1.1 christos device_error(me, "missing or invalid interrupt-ranges property entry %d", reg_nr);
432 1.1 christos /* first reg entry specifies the address of both the IDU and the
433 1.1 christos first set of ISU registers, adjust things accordingly */
434 1.1 christos if (reg_nr == idu_reg) {
435 1.1 christos opic->idu.reg = opic->isu_block[isb].reg;
436 1.1 christos opic->idu.space = opic->isu_block[isb].space;
437 1.1 christos opic->idu.address = opic->isu_block[isb].address;
438 1.1 christos opic->idu.size = opic->isu_block[isb].size;
439 1.1 christos opic->isu_block[isb].address += idu_isu_base;
440 1.1 christos opic->isu_block[isb].size = opic->isu_block[isb].range * (16 + 16);
441 1.1 christos }
442 1.1 christos /* was this a valid reg entry? */
443 1.1 christos if (opic->isu_block[isb].range == 0) {
444 1.1 christos opic->nr_isu_blocks -= 1;
445 1.1 christos }
446 1.1 christos else {
447 1.1 christos opic->nr_external_interrupts += opic->isu_block[isb].range;
448 1.1 christos isb++;
449 1.1 christos }
450 1.1 christos reg_nr++;
451 1.1 christos }
452 1.1 christos }
453 1.1 christos DTRACE(opic, ("interrupt source unit block - effective number of blocks %d\n",
454 1.1 christos (int)opic->nr_isu_blocks));
455 1.1 christos
456 1.1 christos
457 1.1 christos /* the number of other interrupts */
458 1.1 christos opic->nr_interprocessor_interrupts = 4;
459 1.1 christos opic->nr_timer_interrupts = 4;
460 1.1 christos
461 1.1 christos
462 1.1 christos /* create space for the interrupt source registers */
463 1.1 christos if (opic->interrupt_source != NULL) {
464 1.1 christos memset(opic->interrupt_source, 0, opic->sizeof_interrupt_source);
465 1.1 christos }
466 1.1 christos else {
467 1.1 christos opic->nr_interrupt_sources = (opic->nr_external_interrupts
468 1.1 christos + opic->nr_interprocessor_interrupts
469 1.1 christos + opic->nr_timer_interrupts);
470 1.1 christos if (opic->nr_interrupt_sources > max_nr_interrupt_sources)
471 1.1 christos device_error(me, "number of interrupt sources exceeded");
472 1.1 christos opic->sizeof_interrupt_source = (sizeof(opic_interrupt_source)
473 1.1 christos * opic->nr_interrupt_sources);
474 1.1 christos opic->interrupt_source = zalloc(opic->sizeof_interrupt_source);
475 1.1 christos opic->external_interrupt_source = opic->interrupt_source;
476 1.1 christos opic->interprocessor_interrupt_source = (opic->external_interrupt_source
477 1.1 christos + opic->nr_external_interrupts);
478 1.1 christos opic->timer_interrupt_source = (opic->interprocessor_interrupt_source
479 1.1 christos + opic->nr_interprocessor_interrupts);
480 1.1 christos }
481 1.1 christos for (i = 0; i < opic->nr_interrupt_sources; i++) {
482 1.1 christos opic_interrupt_source *source = &opic->interrupt_source[i];
483 1.1 christos source->nr = i;
484 1.1 christos source->is_masked = isu_mask_bit;
485 1.1 christos }
486 1.1 christos DTRACE(opic, ("interrupt sources - external %d, timer %d, ipi %d, total %d\n",
487 1.1 christos opic->nr_external_interrupts,
488 1.1 christos opic->nr_timer_interrupts,
489 1.1 christos opic->nr_interprocessor_interrupts,
490 1.1 christos opic->nr_interrupt_sources));
491 1.1 christos
492 1.1 christos
493 1.1 christos /* timers or interprocessor interrupts */
494 1.1 christos if (opic->timer != NULL)
495 1.1 christos memset(opic->timer, 0, opic->sizeof_timer);
496 1.1 christos else {
497 1.1 christos opic->nr_timer_interrupts = 4;
498 1.1 christos opic->sizeof_timer = sizeof(opic_timer) * opic->nr_timer_interrupts;
499 1.1 christos opic->timer = zalloc(opic->sizeof_timer);
500 1.1 christos }
501 1.1 christos for (i = 0; i < opic->nr_timer_interrupts; i++) {
502 1.1 christos opic_timer *timer = &opic->timer[i];
503 1.1 christos timer->nr = i;
504 1.1 christos timer->me = me;
505 1.1 christos timer->opic = opic;
506 1.1 christos timer->inhibited = 1;
507 1.1 christos timer->interrupt_source = &opic->timer_interrupt_source[i];
508 1.1 christos }
509 1.1 christos if (device_find_property(me, "timer-frequency"))
510 1.1 christos opic->timer_frequency = device_find_integer_property(me, "timer-frequency");
511 1.1 christos else
512 1.1 christos opic->timer_frequency = 1;
513 1.1 christos
514 1.1 christos
515 1.1 christos /* create space for the interrupt destination registers */
516 1.1 christos if (opic->interrupt_destination != NULL) {
517 1.1 christos memset(opic->interrupt_destination, 0, opic->sizeof_interrupt_destination);
518 1.1 christos }
519 1.1 christos else {
520 1.1 christos opic->nr_interrupt_destinations = tree_find_integer_property(me, "/openprom/options/smp");
521 1.1 christos opic->sizeof_interrupt_destination = (sizeof(opic_interrupt_destination)
522 1.1 christos * opic->nr_interrupt_destinations);
523 1.1 christos opic->interrupt_destination = zalloc(opic->sizeof_interrupt_destination);
524 1.1 christos if (opic->nr_interrupt_destinations > max_nr_interrupt_destinations)
525 1.1 christos device_error(me, "number of interrupt destinations exceeded");
526 1.1 christos }
527 1.1 christos for (i = 0; i < opic->nr_interrupt_destinations; i++) {
528 1.1 christos opic_interrupt_destination *dest = &opic->interrupt_destination[i];
529 1.1 christos dest->bit = (1 << i);
530 1.1 christos dest->nr = i;
531 1.1 christos dest->init_port = (device_interrupt_decode(me, "init0", output_port)
532 1.1 christos + i);
533 1.1 christos dest->intr_port = (device_interrupt_decode(me, "intr0", output_port)
534 1.1 christos + i);
535 1.1 christos dest->base_priority = max_nr_task_priorities - 1;
536 1.1 christos }
537 1.1 christos DTRACE(opic, ("interrupt destinations - total %d\n",
538 1.1 christos (int)opic->nr_interrupt_destinations));
539 1.1 christos
540 1.1 christos
541 1.1 christos /* verify and print out the ISU's */
542 1.1 christos for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
543 1.1 christos unsigned correct_size;
544 1.1 christos if ((opic->isu_block[isb].address % opic_alignment) != 0)
545 1.1 christos device_error(me, "interrupt source unit %d address not aligned to %d byte boundary",
546 1.1 christos isb, opic_alignment);
547 1.1 christos correct_size = opic->isu_block[isb].range * sizeof_isu_register_block;
548 1.1 christos if (opic->isu_block[isb].size != correct_size)
549 1.1 christos device_error(me, "interrupt source unit %d (reg %d) has an incorrect size, should be 0x%x",
550 1.1 christos isb, opic->isu_block[isb].reg, correct_size);
551 1.1 christos DTRACE(opic, ("interrupt source unit block %ld - address %d:0x%lx, size 0x%lx, int-number %ld, range %ld\n",
552 1.1 christos (long)isb,
553 1.1 christos (int)opic->isu_block[isb].space,
554 1.1 christos (unsigned long)opic->isu_block[isb].address,
555 1.1 christos (unsigned long)opic->isu_block[isb].size,
556 1.1 christos (long)opic->isu_block[isb].int_number,
557 1.1 christos (long)opic->isu_block[isb].range));
558 1.1 christos }
559 1.1 christos
560 1.1 christos
561 1.1 christos /* verify and print out the IDU */
562 1.1 christos {
563 1.1 christos unsigned correct_size;
564 1.1 christos unsigned alternate_size;
565 1.1 christos if ((opic->idu.address % opic_alignment) != 0)
566 1.1 christos device_error(me, "interrupt delivery unit not aligned to %d byte boundary",
567 1.1 christos opic_alignment);
568 1.1 christos correct_size = (idu_per_processor_register_base
569 1.1 christos + (sizeof_idu_per_processor_register_block
570 1.1 christos * opic->nr_interrupt_destinations));
571 1.1 christos alternate_size = (idu_per_processor_register_base
572 1.1 christos + (sizeof_idu_per_processor_register_block
573 1.1 christos * max_nr_interrupt_destinations));
574 1.1 christos if (opic->idu.size != correct_size
575 1.1 christos && opic->idu.size != alternate_size)
576 1.1 christos device_error(me, "interrupt delivery unit has incorrect size, should be 0x%x or 0x%x",
577 1.1 christos correct_size, alternate_size);
578 1.1 christos DTRACE(opic, ("interrupt delivery unit - address %d:0x%lx, size 0x%lx\n",
579 1.1 christos (int)opic->idu.space,
580 1.1 christos (unsigned long)opic->idu.address,
581 1.1 christos (unsigned long)opic->idu.size));
582 1.1 christos }
583 1.1 christos
584 1.1 christos /* initialize the init interrupts */
585 1.1 christos opic->init = 0;
586 1.1 christos
587 1.1 christos
588 1.1 christos /* vendor ident */
589 1.1 christos if (device_find_property(me, "vendor-identification") != NULL)
590 1.1 christos opic->vendor_identification = device_find_integer_property(me, "vendor-identification");
591 1.1 christos else
592 1.1 christos opic->vendor_identification = 0;
593 1.1 christos
594 1.1 christos /* misc registers */
595 1.1 christos opic->spurious_vector = 0xff;
596 1.1 christos
597 1.1 christos }
598 1.1 christos
599 1.1 christos
600 1.1 christos /* interrupt related actions */
601 1.1 christos
602 1.1 christos static void
603 1.1 christos assert_interrupt(device *me,
604 1.1 christos hw_opic_device *opic,
605 1.1 christos opic_interrupt_destination *dest)
606 1.1 christos {
607 1.1 christos ASSERT(dest >= opic->interrupt_destination);
608 1.1 christos ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations);
609 1.1 christos DTRACE(opic, ("assert interrupt - intr port %d\n", dest->intr_port));
610 1.1 christos device_interrupt_event(me, dest->intr_port, 1, NULL, 0);
611 1.1 christos }
612 1.1 christos
613 1.1 christos
614 1.1 christos static void
615 1.1 christos negate_interrupt(device *me,
616 1.1 christos hw_opic_device *opic,
617 1.1 christos opic_interrupt_destination *dest)
618 1.1 christos {
619 1.1 christos ASSERT(dest >= opic->interrupt_destination);
620 1.1 christos ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations);
621 1.1 christos DTRACE(opic, ("negate interrupt - intr port %d\n", dest->intr_port));
622 1.1 christos device_interrupt_event(me, dest->intr_port, 0, NULL, 0);
623 1.1 christos }
624 1.1 christos
625 1.1 christos
626 1.1 christos static int
627 1.1 christos can_deliver(device *me,
628 1.1 christos opic_interrupt_source *source,
629 1.1 christos opic_interrupt_destination *dest)
630 1.1 christos {
631 1.1 christos return (source != NULL && dest != NULL
632 1.1 christos && source->priority > dest->base_priority
633 1.1 christos && (dest->current_in_service == NULL
634 1.1 christos || source->priority > dest->current_in_service->priority));
635 1.1 christos }
636 1.1 christos
637 1.1 christos
638 1.1 christos static unsigned
639 1.1 christos deliver_pending(device *me,
640 1.1 christos hw_opic_device *opic,
641 1.1 christos opic_interrupt_destination *dest)
642 1.1 christos {
643 1.1 christos ASSERT(can_deliver(me, dest->current_pending, dest));
644 1.1 christos dest->current_in_service = dest->current_pending;
645 1.1 christos dest->current_in_service->in_service |= dest->bit;
646 1.1 christos if (!dest->current_pending->is_level_triggered) {
647 1.1 christos if (dest->current_pending->is_multicast)
648 1.1 christos dest->current_pending->pending &= ~dest->bit;
649 1.1 christos else
650 1.1 christos dest->current_pending->pending = 0;
651 1.1 christos }
652 1.1 christos dest->current_pending = NULL;
653 1.1 christos negate_interrupt(me, opic, dest);
654 1.1 christos return dest->current_in_service->vector;
655 1.1 christos }
656 1.1 christos
657 1.1 christos
658 1.1 christos typedef enum {
659 1.1 christos pending_interrupt,
660 1.1 christos in_service_interrupt,
661 1.1 christos } interrupt_class;
662 1.1 christos
663 1.1 christos static opic_interrupt_source *
664 1.1 christos find_interrupt_for_dest(device *me,
665 1.1 christos hw_opic_device *opic,
666 1.1 christos opic_interrupt_destination *dest,
667 1.1 christos interrupt_class class)
668 1.1 christos {
669 1.1 christos int i;
670 1.1 christos opic_interrupt_source *pending = NULL;
671 1.1 christos for (i = 0; i < opic->nr_interrupt_sources; i++) {
672 1.1 christos opic_interrupt_source *src = &opic->interrupt_source[i];
673 1.1 christos /* is this a potential hit? */
674 1.1 christos switch (class) {
675 1.1 christos case in_service_interrupt:
676 1.1 christos if ((src->in_service & dest->bit) == 0)
677 1.1 christos continue;
678 1.1 christos break;
679 1.1 christos case pending_interrupt:
680 1.1 christos if ((src->pending & dest->bit) == 0)
681 1.1 christos continue;
682 1.1 christos break;
683 1.1 christos }
684 1.1 christos /* see if it is the highest priority */
685 1.1 christos if (pending == NULL)
686 1.1 christos pending = src;
687 1.1 christos else if (src->priority > pending->priority)
688 1.1 christos pending = src;
689 1.1 christos }
690 1.1 christos return pending;
691 1.1 christos }
692 1.1 christos
693 1.1 christos
694 1.1 christos static opic_interrupt_destination *
695 1.1 christos find_lowest_dest(device *me,
696 1.1 christos hw_opic_device *opic,
697 1.1 christos opic_interrupt_source *src)
698 1.1 christos {
699 1.1 christos int i;
700 1.1 christos opic_interrupt_destination *lowest = NULL;
701 1.1 christos for (i = 0; i < opic->nr_interrupt_destinations; i++) {
702 1.1 christos opic_interrupt_destination *dest = &opic->interrupt_destination[i];
703 1.1 christos if (src->destination & dest->bit) {
704 1.1 christos if (dest->base_priority < src->priority) {
705 1.1 christos if (lowest == NULL)
706 1.1 christos lowest = dest;
707 1.1 christos else if (lowest->base_priority > dest->base_priority)
708 1.1 christos lowest = dest;
709 1.1 christos else if (lowest->current_in_service != NULL
710 1.1 christos && dest->current_in_service == NULL)
711 1.1 christos lowest = dest; /* not doing anything */
712 1.1 christos else if (lowest->current_in_service != NULL
713 1.1 christos && dest->current_in_service != NULL
714 1.1 christos && (lowest->current_in_service->priority
715 1.1 christos > dest->current_in_service->priority))
716 1.1 christos lowest = dest; /* less urgent */
717 1.1 christos /* FIXME - need to be more fair */
718 1.1 christos }
719 1.1 christos }
720 1.1 christos }
721 1.1 christos return lowest;
722 1.1 christos }
723 1.1 christos
724 1.1 christos
725 1.1 christos static void
726 1.1 christos handle_interrupt(device *me,
727 1.1 christos hw_opic_device *opic,
728 1.1 christos opic_interrupt_source *src,
729 1.1 christos int asserted)
730 1.1 christos {
731 1.1 christos if (src->is_masked) {
732 1.1 christos DTRACE(opic, ("interrupt %d - ignore masked\n", src->nr));
733 1.1 christos }
734 1.1 christos else if (src->is_multicast) {
735 1.1 christos /* always try to deliver multicast interrupts - just easier */
736 1.1 christos int i;
737 1.1 christos ASSERT(!src->is_level_triggered);
738 1.1 christos ASSERT(src->is_positive_polarity);
739 1.1 christos ASSERT(asserted);
740 1.1 christos for (i = 0; i < opic->nr_interrupt_destinations; i++) {
741 1.1 christos opic_interrupt_destination *dest = &opic->interrupt_destination[i];
742 1.1 christos if (src->destination & dest->bit) {
743 1.1 christos if (src->pending & dest->bit) {
744 1.1 christos DTRACE(opic, ("interrupt %d - multicast still pending to %d\n",
745 1.1 christos src->nr, dest->nr));
746 1.1 christos }
747 1.1 christos else if (can_deliver(me, src, dest)) {
748 1.1 christos dest->current_pending = src;
749 1.1 christos src->pending |= dest->bit;
750 1.1 christos assert_interrupt(me, opic, dest);
751 1.1 christos DTRACE(opic, ("interrupt %d - multicast to %d\n",
752 1.1 christos src->nr, dest->nr));
753 1.1 christos }
754 1.1 christos else {
755 1.1 christos src->pending |= dest->bit;
756 1.1 christos DTRACE(opic, ("interrupt %d - multicast pending to %d\n",
757 1.1 christos src->nr, dest->nr));
758 1.1 christos }
759 1.1 christos }
760 1.1 christos }
761 1.1 christos }
762 1.1 christos else if (src->is_level_triggered
763 1.1 christos && src->is_positive_polarity
764 1.1 christos && !asserted) {
765 1.1 christos if (src->pending)
766 1.1 christos DTRACE(opic, ("interrupt %d - ignore withdrawn (active high)\n",
767 1.1 christos src->nr));
768 1.1 christos else
769 1.1 christos DTRACE(opic, ("interrupt %d - ignore low level (active high)\n",
770 1.1 christos src->nr));
771 1.1 christos ASSERT(!src->is_multicast);
772 1.1 christos src->pending = 0;
773 1.1 christos }
774 1.1 christos else if (src->is_level_triggered
775 1.1 christos && !src->is_positive_polarity
776 1.1 christos && asserted) {
777 1.1 christos if (src->pending)
778 1.1 christos DTRACE(opic, ("interrupt %d - ignore withdrawn (active low)\n",
779 1.1 christos src->nr));
780 1.1 christos else
781 1.1 christos DTRACE(opic, ("interrupt %d - ignore high level (active low)\n",
782 1.1 christos src->nr));
783 1.1 christos
784 1.1 christos ASSERT(!src->is_multicast);
785 1.1 christos src->pending = 0;
786 1.1 christos }
787 1.1 christos else if (!src->is_level_triggered
788 1.1 christos && src->is_positive_polarity
789 1.1 christos && !asserted) {
790 1.1 christos DTRACE(opic, ("interrupt %d - ignore falling edge (positive edge trigered)\n",
791 1.1 christos src->nr));
792 1.1 christos }
793 1.1 christos else if (!src->is_level_triggered
794 1.1 christos && !src->is_positive_polarity
795 1.1 christos && asserted) {
796 1.1 christos DTRACE(opic, ("interrupt %d - ignore rising edge (negative edge trigered)\n",
797 1.1 christos src->nr));
798 1.1 christos }
799 1.1 christos else if (src->in_service != 0) {
800 1.1 christos /* leave the interrupt where it is */
801 1.1 christos ASSERT(!src->is_multicast);
802 1.1 christos ASSERT(src->pending == 0 || src->pending == src->in_service);
803 1.1 christos src->pending = src->in_service;
804 1.1 christos DTRACE(opic, ("interrupt %ld - ignore already in service to 0x%lx\n",
805 1.1 christos (long)src->nr, (long)src->in_service));
806 1.1 christos }
807 1.1 christos else if (src->pending != 0) {
808 1.1 christos DTRACE(opic, ("interrupt %ld - ignore still pending to 0x%lx\n",
809 1.1 christos (long)src->nr, (long)src->pending));
810 1.1 christos }
811 1.1 christos else {
812 1.1 christos /* delivery is needed */
813 1.1 christos opic_interrupt_destination *dest = find_lowest_dest(me, opic, src);
814 1.1 christos if (can_deliver(me, src, dest)) {
815 1.1 christos dest->current_pending = src;
816 1.1 christos src->pending = dest->bit;
817 1.1 christos DTRACE(opic, ("interrupt %d - delivered to %d\n", src->nr, dest->nr));
818 1.1 christos assert_interrupt(me, opic, dest);
819 1.1 christos }
820 1.1 christos else {
821 1.1 christos src->pending = src->destination; /* any can take this */
822 1.1 christos DTRACE(opic, ("interrupt %ld - pending to 0x%lx\n",
823 1.1 christos (long)src->nr, (long)src->pending));
824 1.1 christos }
825 1.1 christos }
826 1.1 christos }
827 1.1 christos
828 1.1 christos static unsigned
829 1.1 christos do_interrupt_acknowledge_register_N_read(device *me,
830 1.1 christos hw_opic_device *opic,
831 1.1 christos int dest_nr)
832 1.1 christos {
833 1.1 christos opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr];
834 1.1 christos unsigned vector;
835 1.1 christos
836 1.1 christos ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations);
837 1.1 christos ASSERT(dest_nr == dest->nr);
838 1.1 christos
839 1.1 christos /* try the current pending */
840 1.1 christos if (can_deliver(me, dest->current_pending, dest)) {
841 1.1 christos ASSERT(dest->current_pending->pending & dest->bit);
842 1.1 christos vector = deliver_pending(me, opic, dest);
843 1.1 christos DTRACE(opic, ("interrupt ack %d - entering %d (pending) - vector %d (%d), priority %d\n",
844 1.1 christos dest->nr,
845 1.1 christos dest->current_in_service->nr,
846 1.1 christos dest->current_in_service->vector, vector,
847 1.1 christos dest->current_in_service->priority));
848 1.1 christos }
849 1.1 christos else {
850 1.1 christos /* try for something else */
851 1.1 christos dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt);
852 1.1 christos if (can_deliver(me, dest->current_pending, dest)) {
853 1.1 christos vector = deliver_pending(me, opic, dest);
854 1.1 christos DTRACE(opic, ("interrupt ack %d - entering %d (not pending) - vector %d (%d), priority %d\n",
855 1.1 christos dest->nr,
856 1.1 christos dest->current_in_service->nr,
857 1.1 christos dest->current_in_service->vector, vector,
858 1.1 christos dest->current_in_service->priority));
859 1.1 christos }
860 1.1 christos else {
861 1.1 christos dest->current_pending = NULL;
862 1.1 christos vector = opic->spurious_vector;
863 1.1 christos DTRACE(opic, ("interrupt ack %d - spurious interrupt %d\n",
864 1.1 christos dest->nr, vector));
865 1.1 christos }
866 1.1 christos }
867 1.1 christos return vector;
868 1.1 christos }
869 1.1 christos
870 1.1 christos
871 1.1 christos static void
872 1.1 christos do_end_of_interrupt_register_N_write(device *me,
873 1.1 christos hw_opic_device *opic,
874 1.1 christos int dest_nr,
875 1.1 christos unsigned reg)
876 1.1 christos {
877 1.1 christos opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr];
878 1.1 christos
879 1.1 christos ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations);
880 1.1 christos ASSERT(dest_nr == dest->nr);
881 1.1 christos
882 1.1 christos /* check the value written is zero */
883 1.1 christos if (reg != 0) {
884 1.1 christos DTRACE(opic, ("eoi %d - ignoring nonzero value\n", dest->nr));
885 1.1 christos }
886 1.1 christos
887 1.1 christos /* user doing wierd things? */
888 1.1 christos if (dest->current_in_service == NULL) {
889 1.1 christos DTRACE(opic, ("eoi %d - strange, no current interrupt\n", dest->nr));
890 1.1 christos return;
891 1.1 christos }
892 1.1 christos
893 1.1 christos /* an internal stuff up? */
894 1.1 christos if (!(dest->current_in_service->in_service & dest->bit)) {
895 1.1 christos device_error(me, "eoi %d - current interrupt not in service", dest->nr);
896 1.1 christos }
897 1.1 christos
898 1.1 christos /* find what was probably the previous in service interrupt */
899 1.1 christos dest->current_in_service->in_service &= ~dest->bit;
900 1.1 christos DTRACE(opic, ("eoi %d - ending %d - priority %d, vector %d\n",
901 1.1 christos dest->nr,
902 1.1 christos dest->current_in_service->nr,
903 1.1 christos dest->current_in_service->priority,
904 1.1 christos dest->current_in_service->vector));
905 1.1 christos dest->current_in_service = find_interrupt_for_dest(me, opic, dest, in_service_interrupt);
906 1.1 christos if (dest->current_in_service != NULL)
907 1.1 christos DTRACE(opic, ("eoi %d - resuming %d - priority %d, vector %d\n",
908 1.1 christos dest->nr,
909 1.1 christos dest->current_in_service->nr,
910 1.1 christos dest->current_in_service->priority,
911 1.1 christos dest->current_in_service->vector));
912 1.1 christos else
913 1.1 christos DTRACE(opic, ("eoi %d - resuming none\n", dest->nr));
914 1.1 christos
915 1.1 christos /* check to see if that shouldn't be interrupted */
916 1.1 christos dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt);
917 1.1 christos if (can_deliver(me, dest->current_pending, dest)) {
918 1.1 christos ASSERT(dest->current_pending->pending & dest->bit);
919 1.1 christos assert_interrupt(me, opic, dest);
920 1.1 christos }
921 1.1 christos else {
922 1.1 christos dest->current_pending = NULL;
923 1.1 christos }
924 1.1 christos }
925 1.1 christos
926 1.1 christos
927 1.1 christos static void
928 1.1 christos decode_opic_address(device *me,
929 1.1 christos hw_opic_device *opic,
930 1.1 christos int space,
931 1.1 christos unsigned_word address,
932 1.1 christos unsigned nr_bytes,
933 1.1 christos opic_register *type,
934 1.1 christos int *index)
935 1.1 christos {
936 1.1 christos int isb = 0;
937 1.1 christos
938 1.1 christos /* is the size valid? */
939 1.1 christos if (nr_bytes != 4) {
940 1.1 christos *type = invalid_opic_register;
941 1.1 christos *index = -1;
942 1.1 christos return;
943 1.1 christos }
944 1.1 christos
945 1.1 christos /* try for a per-processor register within the interrupt delivery
946 1.1 christos unit */
947 1.1 christos if (space == opic->idu.space
948 1.1 christos && address >= (opic->idu.address + idu_per_processor_register_base)
949 1.1 christos && address < (opic->idu.address + idu_per_processor_register_base
950 1.1 christos + (sizeof_idu_per_processor_register_block
951 1.1 christos * opic->nr_interrupt_destinations))) {
952 1.1 christos unsigned_word block_offset = (address
953 1.1 christos - opic->idu.address
954 1.1 christos - idu_per_processor_register_base);
955 1.1 christos unsigned_word offset = block_offset % sizeof_idu_per_processor_register_block;
956 1.1 christos *index = block_offset / sizeof_idu_per_processor_register_block;
957 1.1 christos switch (offset) {
958 1.1 christos case 0x040:
959 1.1 christos *type = ipi_N_dispatch_register;
960 1.1 christos *index = 0;
961 1.1 christos break;
962 1.1 christos case 0x050:
963 1.1 christos *type = ipi_N_dispatch_register;
964 1.1 christos *index = 1;
965 1.1 christos break;
966 1.1 christos case 0x060:
967 1.1 christos *type = ipi_N_dispatch_register;
968 1.1 christos *index = 2;
969 1.1 christos break;
970 1.1 christos case 0x070:
971 1.1 christos *type = ipi_N_dispatch_register;
972 1.1 christos *index = 3;
973 1.1 christos break;
974 1.1 christos case 0x080:
975 1.1 christos *type = current_task_priority_register_N;
976 1.1 christos break;
977 1.1 christos case 0x0a0:
978 1.1 christos *type = interrupt_acknowledge_register_N;
979 1.1 christos break;
980 1.1 christos case 0x0b0:
981 1.1 christos *type = end_of_interrupt_register_N;
982 1.1 christos break;
983 1.1 christos default:
984 1.1 christos *type = invalid_opic_register;
985 1.1 christos break;
986 1.1 christos }
987 1.1 christos DTRACE(opic, ("per-processor register %d:0x%lx - %s[%d]\n",
988 1.1 christos space, (unsigned long)address,
989 1.1 christos opic_register_name(*type),
990 1.1 christos *index));
991 1.1 christos return;
992 1.1 christos }
993 1.1 christos
994 1.1 christos /* try for an interrupt source unit */
995 1.1 christos for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
996 1.1 christos if (opic->isu_block[isb].space == space
997 1.1 christos && address >= opic->isu_block[isb].address
998 1.1 christos && address < (opic->isu_block[isb].address + opic->isu_block[isb].size)) {
999 1.1 christos unsigned_word block_offset = address - opic->isu_block[isb].address;
1000 1.1 christos unsigned_word offset = block_offset % sizeof_isu_register_block;
1001 1.1 christos *index = (opic->isu_block[isb].int_number
1002 1.1 christos + (block_offset / sizeof_isu_register_block));
1003 1.1 christos switch (offset) {
1004 1.1 christos case 0x00:
1005 1.1 christos *type = interrupt_source_N_vector_priority_register;
1006 1.1 christos break;
1007 1.1 christos case 0x10:
1008 1.1 christos *type = interrupt_source_N_destination_register;
1009 1.1 christos break;
1010 1.1 christos default:
1011 1.1 christos *type = invalid_opic_register;
1012 1.1 christos break;
1013 1.1 christos }
1014 1.1 christos DTRACE(opic, ("isu register %d:0x%lx - %s[%d]\n",
1015 1.1 christos space, (unsigned long)address,
1016 1.1 christos opic_register_name(*type),
1017 1.1 christos *index));
1018 1.1 christos return;
1019 1.1 christos }
1020 1.1 christos }
1021 1.1 christos
1022 1.1 christos /* try for a timer */
1023 1.1 christos if (space == opic->idu.space
1024 1.1 christos && address >= (opic->idu.address + idu_timer_base)
1025 1.1 christos && address < (opic->idu.address + idu_timer_base
1026 1.1 christos + opic->nr_timer_interrupts * sizeof_timer_register_block)) {
1027 1.1 christos unsigned_word offset = address % sizeof_timer_register_block;
1028 1.1 christos *index = ((address - opic->idu.address - idu_timer_base)
1029 1.1 christos / sizeof_timer_register_block);
1030 1.1 christos switch (offset) {
1031 1.1 christos case 0x00:
1032 1.1 christos *type = timer_N_current_count_register;
1033 1.1 christos break;
1034 1.1 christos case 0x10:
1035 1.1 christos *type = timer_N_base_count_register;
1036 1.1 christos break;
1037 1.1 christos case 0x20:
1038 1.1 christos *type = timer_N_vector_priority_register;
1039 1.1 christos break;
1040 1.1 christos case 0x30:
1041 1.1 christos *type = timer_N_destination_register;
1042 1.1 christos break;
1043 1.1 christos default:
1044 1.1 christos *type = invalid_opic_register;
1045 1.1 christos break;
1046 1.1 christos }
1047 1.1 christos DTRACE(opic, ("timer register %d:0x%lx - %s[%d]\n",
1048 1.1 christos space, (unsigned long)address,
1049 1.1 christos opic_register_name(*type),
1050 1.1 christos *index));
1051 1.1 christos return;
1052 1.1 christos }
1053 1.1 christos
1054 1.1 christos /* finally some other misc global register */
1055 1.1 christos if (space == opic->idu.space
1056 1.1 christos && address >= opic->idu.address
1057 1.1 christos && address < opic->idu.address + opic->idu.size) {
1058 1.1 christos unsigned_word block_offset = address - opic->idu.address;
1059 1.1 christos switch (block_offset) {
1060 1.1 christos case 0x010f0:
1061 1.1 christos *type = timer_frequency_reporting_register;
1062 1.1 christos *index = -1;
1063 1.1 christos break;
1064 1.1 christos case 0x010e0:
1065 1.1 christos *type = spurious_vector_register;
1066 1.1 christos *index = -1;
1067 1.1 christos break;
1068 1.1 christos case 0x010d0:
1069 1.1 christos case 0x010c0:
1070 1.1 christos case 0x010b0:
1071 1.1 christos case 0x010a0:
1072 1.1 christos *type = ipi_N_vector_priority_register;
1073 1.1 christos *index = (block_offset - 0x010a0) / 16;
1074 1.1 christos break;
1075 1.1 christos case 0x01090:
1076 1.1 christos *type = processor_init_register;
1077 1.1 christos *index = -1;
1078 1.1 christos break;
1079 1.1 christos case 0x01080:
1080 1.1 christos *type = vendor_identification_register;
1081 1.1 christos *index = -1;
1082 1.1 christos break;
1083 1.1 christos case 0x01020:
1084 1.1 christos *type = global_configuration_register_N;
1085 1.1 christos *index = 0;
1086 1.1 christos break;
1087 1.1 christos case 0x01000:
1088 1.1 christos *type = feature_reporting_register_N;
1089 1.1 christos *index = 0;
1090 1.1 christos break;
1091 1.1 christos default:
1092 1.1 christos *type = invalid_opic_register;
1093 1.1 christos *index = -1;
1094 1.1 christos break;
1095 1.1 christos }
1096 1.1 christos DTRACE(opic, ("global register %d:0x%lx - %s[%d]\n",
1097 1.1 christos space, (unsigned long)address,
1098 1.1 christos opic_register_name(*type),
1099 1.1 christos *index));
1100 1.1 christos return;
1101 1.1 christos }
1102 1.1 christos
1103 1.1 christos /* nothing matched */
1104 1.1 christos *type = invalid_opic_register;
1105 1.1 christos DTRACE(opic, ("invalid register %d:0x%lx\n",
1106 1.1 christos space, (unsigned long)address));
1107 1.1 christos return;
1108 1.1 christos }
1109 1.1 christos
1110 1.1 christos
1111 1.1 christos /* Processor init register:
1112 1.1 christos
1113 1.1 christos The bits in this register (one per processor) are directly wired to
1114 1.1 christos output "init" interrupt ports. */
1115 1.1 christos
1116 1.1 christos static unsigned
1117 1.1 christos do_processor_init_register_read(device *me,
1118 1.1 christos hw_opic_device *opic)
1119 1.1 christos {
1120 1.1 christos unsigned reg = opic->init;
1121 1.1 christos DTRACE(opic, ("processor init register - read 0x%lx\n",
1122 1.1 christos (long)reg));
1123 1.1 christos return reg;
1124 1.1 christos }
1125 1.1 christos
1126 1.1 christos static void
1127 1.1 christos do_processor_init_register_write(device *me,
1128 1.1 christos hw_opic_device *opic,
1129 1.1 christos unsigned reg)
1130 1.1 christos {
1131 1.1 christos int i;
1132 1.1 christos for (i = 0; i < opic->nr_interrupt_destinations; i++) {
1133 1.1 christos opic_interrupt_destination *dest = &opic->interrupt_destination[i];
1134 1.1 christos if ((reg & dest->bit) != (opic->init & dest->bit)) {
1135 1.1 christos if (reg & dest->bit) {
1136 1.1 christos DTRACE(opic, ("processor init register - write 0x%lx - asserting init%d\n",
1137 1.1 christos (long)reg, i));
1138 1.1 christos opic->init |= dest->bit;
1139 1.1 christos device_interrupt_event(me, dest->init_port, 1, NULL, 0);
1140 1.1 christos }
1141 1.1 christos else {
1142 1.1 christos DTRACE(opic, ("processor init register - write 0x%lx - negating init%d\n",
1143 1.1 christos (long)reg, i));
1144 1.1 christos opic->init &= ~dest->bit;
1145 1.1 christos device_interrupt_event(me, dest->init_port, 0, NULL, 0);
1146 1.1 christos }
1147 1.1 christos }
1148 1.1 christos }
1149 1.1 christos }
1150 1.1 christos
1151 1.1 christos
1152 1.1 christos
1153 1.1 christos /* Interrupt Source Vector/Priority Register: */
1154 1.1 christos
1155 1.1 christos static unsigned
1156 1.1 christos read_vector_priority_register(device *me,
1157 1.1 christos hw_opic_device *opic,
1158 1.1 christos opic_interrupt_source *interrupt,
1159 1.1 christos const char *reg_name,
1160 1.1 christos int reg_index)
1161 1.1 christos {
1162 1.1 christos unsigned reg;
1163 1.1 christos reg = 0;
1164 1.1 christos reg |= interrupt->is_masked;
1165 1.1 christos reg |= (interrupt->in_service || interrupt->pending
1166 1.1 christos ? isu_active_bit : 0); /* active */
1167 1.1 christos reg |= interrupt->is_multicast;
1168 1.1 christos reg |= interrupt->is_positive_polarity;
1169 1.1 christos reg |= interrupt->is_level_triggered; /* sense? */
1170 1.1 christos reg |= interrupt->priority << isu_priority_shift;
1171 1.1 christos reg |= interrupt->vector;
1172 1.1 christos DTRACE(opic, ("%s %d vector/priority register - read 0x%lx\n",
1173 1.1 christos reg_name, reg_index, (unsigned long)reg));
1174 1.1 christos return reg;
1175 1.1 christos }
1176 1.1 christos
1177 1.1 christos static unsigned
1178 1.1 christos do_interrupt_source_N_vector_priority_register_read(device *me,
1179 1.1 christos hw_opic_device *opic,
1180 1.1 christos int index)
1181 1.1 christos {
1182 1.1 christos unsigned reg;
1183 1.1 christos ASSERT(index < opic->nr_external_interrupts);
1184 1.1 christos reg = read_vector_priority_register(me, opic,
1185 1.1 christos &opic->interrupt_source[index],
1186 1.1 christos "interrupt source", index);
1187 1.1 christos return reg;
1188 1.1 christos }
1189 1.1 christos
1190 1.1 christos static void
1191 1.1 christos write_vector_priority_register(device *me,
1192 1.1 christos hw_opic_device *opic,
1193 1.1 christos opic_interrupt_source *interrupt,
1194 1.1 christos unsigned reg,
1195 1.1 christos const char *reg_name,
1196 1.1 christos int reg_index)
1197 1.1 christos {
1198 1.1 christos interrupt->is_masked = (reg & isu_mask_bit);
1199 1.1 christos interrupt->is_multicast = (reg & isu_multicast_bit);
1200 1.1 christos interrupt->is_positive_polarity = (reg & isu_positive_polarity_bit);
1201 1.1 christos interrupt->is_level_triggered = (reg & isu_level_triggered_bit);
1202 1.1 christos interrupt->priority = ((reg >> isu_priority_shift)
1203 1.1 christos % max_nr_task_priorities);
1204 1.1 christos interrupt->vector = (reg & isu_vector_bits);
1205 1.1 christos DTRACE(opic, ("%s %d vector/priority register - write 0x%lx - %s%s%s-polarity, %s-triggered, priority %ld vector %ld\n",
1206 1.1 christos reg_name,
1207 1.1 christos reg_index,
1208 1.1 christos (unsigned long)reg,
1209 1.1 christos interrupt->is_masked ? "masked, " : "",
1210 1.1 christos interrupt->is_multicast ? "multicast, " : "",
1211 1.1 christos interrupt->is_positive_polarity ? "positive" : "negative",
1212 1.1 christos interrupt->is_level_triggered ? "level" : "edge",
1213 1.1 christos (long)interrupt->priority,
1214 1.1 christos (long)interrupt->vector));
1215 1.1 christos }
1216 1.1 christos
1217 1.1 christos static void
1218 1.1 christos do_interrupt_source_N_vector_priority_register_write(device *me,
1219 1.1 christos hw_opic_device *opic,
1220 1.1 christos int index,
1221 1.1 christos unsigned reg)
1222 1.1 christos {
1223 1.1 christos ASSERT(index < opic->nr_external_interrupts);
1224 1.1 christos reg &= ~isu_multicast_bit; /* disable multicast */
1225 1.1 christos write_vector_priority_register(me, opic,
1226 1.1 christos &opic->interrupt_source[index],
1227 1.1 christos reg, "interrupt source", index);
1228 1.1 christos }
1229 1.1 christos
1230 1.1 christos
1231 1.1 christos
1232 1.1 christos /* Interrupt Source Destination Register: */
1233 1.1 christos
1234 1.1 christos static unsigned
1235 1.1 christos read_destination_register(device *me,
1236 1.1 christos hw_opic_device *opic,
1237 1.1 christos opic_interrupt_source *interrupt,
1238 1.1 christos const char *reg_name,
1239 1.1 christos int reg_index)
1240 1.1 christos {
1241 1.1 christos unsigned long reg;
1242 1.1 christos reg = interrupt->destination;
1243 1.1 christos DTRACE(opic, ("%s %d destination register - read 0x%lx\n",
1244 1.1 christos reg_name, reg_index, reg));
1245 1.1 christos return reg;
1246 1.1 christos }
1247 1.1 christos
1248 1.1 christos static unsigned
1249 1.1 christos do_interrupt_source_N_destination_register_read(device *me,
1250 1.1 christos hw_opic_device *opic,
1251 1.1 christos int index)
1252 1.1 christos {
1253 1.1 christos unsigned reg;
1254 1.1 christos ASSERT(index < opic->nr_external_interrupts);
1255 1.1 christos reg = read_destination_register(me, opic, &opic->external_interrupt_source[index],
1256 1.1 christos "interrupt source", index);
1257 1.1 christos return reg;
1258 1.1 christos }
1259 1.1 christos
1260 1.1 christos static void
1261 1.1 christos write_destination_register(device *me,
1262 1.1 christos hw_opic_device *opic,
1263 1.1 christos opic_interrupt_source *interrupt,
1264 1.1 christos unsigned reg,
1265 1.1 christos const char *reg_name,
1266 1.1 christos int reg_index)
1267 1.1 christos {
1268 1.1 christos reg &= (1 << opic->nr_interrupt_destinations) - 1; /* mask out invalid */
1269 1.1 christos DTRACE(opic, ("%s %d destination register - write 0x%x\n",
1270 1.1 christos reg_name, reg_index, reg));
1271 1.1 christos interrupt->destination = reg;
1272 1.1 christos }
1273 1.1 christos
1274 1.1 christos static void
1275 1.1 christos do_interrupt_source_N_destination_register_write(device *me,
1276 1.1 christos hw_opic_device *opic,
1277 1.1 christos int index,
1278 1.1 christos unsigned reg)
1279 1.1 christos {
1280 1.1 christos ASSERT(index < opic->nr_external_interrupts);
1281 1.1 christos write_destination_register(me, opic, &opic->external_interrupt_source[index],
1282 1.1 christos reg, "interrupt source", index);
1283 1.1 christos }
1284 1.1 christos
1285 1.1 christos
1286 1.1 christos
1287 1.1 christos /* Spurious vector register: */
1288 1.1 christos
1289 1.1 christos static unsigned
1290 1.1 christos do_spurious_vector_register_read(device *me,
1291 1.1 christos hw_opic_device *opic)
1292 1.1 christos {
1293 1.1 christos unsigned long reg = opic->spurious_vector;
1294 1.1 christos DTRACE(opic, ("spurious vector register - read 0x%lx\n", reg));
1295 1.1 christos return reg;
1296 1.1 christos }
1297 1.1 christos
1298 1.1 christos static void
1299 1.1 christos do_spurious_vector_register_write(device *me,
1300 1.1 christos hw_opic_device *opic,
1301 1.1 christos unsigned reg)
1302 1.1 christos {
1303 1.1 christos reg &= 0xff; /* mask off invalid */
1304 1.1 christos DTRACE(opic, ("spurious vector register - write 0x%x\n", reg));
1305 1.1 christos opic->spurious_vector = reg;
1306 1.1 christos }
1307 1.1 christos
1308 1.1 christos
1309 1.1 christos
1310 1.1 christos /* current task priority register: */
1311 1.1 christos
1312 1.1 christos static unsigned
1313 1.1 christos do_current_task_priority_register_N_read(device *me,
1314 1.1 christos hw_opic_device *opic,
1315 1.1 christos int index)
1316 1.1 christos {
1317 1.1 christos opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index];
1318 1.1 christos unsigned reg;
1319 1.1 christos ASSERT(index >= 0 && index < opic->nr_interrupt_destinations);
1320 1.1 christos reg = interrupt_destination->base_priority;
1321 1.1 christos DTRACE(opic, ("current task priority register %d - read 0x%x\n", index, reg));
1322 1.1 christos return reg;
1323 1.1 christos }
1324 1.1 christos
1325 1.1 christos static void
1326 1.1 christos do_current_task_priority_register_N_write(device *me,
1327 1.1 christos hw_opic_device *opic,
1328 1.1 christos int index,
1329 1.1 christos unsigned reg)
1330 1.1 christos {
1331 1.1 christos opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index];
1332 1.1 christos ASSERT(index >= 0 && index < opic->nr_interrupt_destinations);
1333 1.1 christos reg %= max_nr_task_priorities;
1334 1.1 christos DTRACE(opic, ("current task priority register %d - write 0x%x\n", index, reg));
1335 1.1 christos interrupt_destination->base_priority = reg;
1336 1.1 christos }
1337 1.1 christos
1338 1.1 christos
1339 1.1 christos
1340 1.1 christos /* Timer Frequency Reporting Register: */
1341 1.1 christos
1342 1.1 christos static unsigned
1343 1.1 christos do_timer_frequency_reporting_register_read(device *me,
1344 1.1 christos hw_opic_device *opic)
1345 1.1 christos {
1346 1.1 christos unsigned reg;
1347 1.1 christos reg = opic->timer_frequency;
1348 1.1 christos DTRACE(opic, ("timer frequency reporting register - read 0x%x\n", reg));
1349 1.1 christos return reg;
1350 1.1 christos }
1351 1.1 christos
1352 1.1 christos static void
1353 1.1 christos do_timer_frequency_reporting_register_write(device *me,
1354 1.1 christos hw_opic_device *opic,
1355 1.1 christos unsigned reg)
1356 1.1 christos {
1357 1.1 christos DTRACE(opic, ("timer frequency reporting register - write 0x%x\n", reg));
1358 1.1 christos opic->timer_frequency = reg;
1359 1.1 christos }
1360 1.1 christos
1361 1.1 christos
1362 1.1 christos /* timer registers: */
1363 1.1 christos
1364 1.1 christos static unsigned
1365 1.1 christos do_timer_N_current_count_register_read(device *me,
1366 1.1 christos hw_opic_device *opic,
1367 1.1 christos int index)
1368 1.1 christos {
1369 1.1 christos opic_timer *timer = &opic->timer[index];
1370 1.1 christos unsigned reg;
1371 1.1 christos ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
1372 1.1 christos if (timer->inhibited)
1373 1.1 christos reg = timer->count; /* stalled value */
1374 1.1 christos else
1375 1.1 christos reg = timer->count - device_event_queue_time(me); /* time remaining */
1376 1.1 christos DTRACE(opic, ("timer %d current count register - read 0x%x\n", index, reg));
1377 1.1 christos return reg;
1378 1.1 christos }
1379 1.1 christos
1380 1.1 christos
1381 1.1 christos static unsigned
1382 1.1 christos do_timer_N_base_count_register_read(device *me,
1383 1.1 christos hw_opic_device *opic,
1384 1.1 christos int index)
1385 1.1 christos {
1386 1.1 christos opic_timer *timer = &opic->timer[index];
1387 1.1 christos unsigned reg;
1388 1.1 christos ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
1389 1.1 christos reg = timer->base_count;
1390 1.1 christos DTRACE(opic, ("timer %d base count register - read 0x%x\n", index, reg));
1391 1.1 christos return reg;
1392 1.1 christos }
1393 1.1 christos
1394 1.1 christos
1395 1.1 christos static void
1396 1.1 christos timer_event(void *data)
1397 1.1 christos {
1398 1.1 christos opic_timer *timer = data;
1399 1.1 christos device *me = timer->me;
1400 1.1 christos if (timer->inhibited)
1401 1.1 christos device_error(timer->me, "internal-error - timer event occured when timer %d inhibited",
1402 1.1 christos timer->nr);
1403 1.1 christos handle_interrupt(timer->me, timer->opic, timer->interrupt_source, 1);
1404 1.1 christos timer->timeout_event = device_event_queue_schedule(me, timer->base_count,
1405 1.1 christos timer_event, timer);
1406 1.1 christos DTRACE(opic, ("timer %d - interrupt at %ld, next at %d\n",
1407 1.1 christos timer->nr, (long)device_event_queue_time(me), timer->base_count));
1408 1.1 christos }
1409 1.1 christos
1410 1.1 christos
1411 1.1 christos static void
1412 1.1 christos do_timer_N_base_count_register_write(device *me,
1413 1.1 christos hw_opic_device *opic,
1414 1.1 christos int index,
1415 1.1 christos unsigned reg)
1416 1.1 christos {
1417 1.1 christos opic_timer *timer = &opic->timer[index];
1418 1.1 christos int inhibit;
1419 1.1 christos ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
1420 1.1 christos inhibit = reg & 0x80000000;
1421 1.1 christos if (timer->inhibited && !inhibit) {
1422 1.1 christos timer->inhibited = 0;
1423 1.1 christos if (timer->timeout_event != NULL)
1424 1.1 christos device_event_queue_deschedule(me, timer->timeout_event);
1425 1.1 christos timer->count = device_event_queue_time(me) + reg;
1426 1.1 christos timer->base_count = reg;
1427 1.1 christos timer->timeout_event = device_event_queue_schedule(me, timer->base_count,
1428 1.1 christos timer_event, (void*)timer);
1429 1.1 christos DTRACE(opic, ("timer %d base count register - write 0x%x - timer started\n",
1430 1.1 christos index, reg));
1431 1.1 christos }
1432 1.1 christos else if (!timer->inhibited && inhibit) {
1433 1.1 christos if (timer->timeout_event != NULL)
1434 1.1 christos device_event_queue_deschedule(me, timer->timeout_event);
1435 1.1 christos timer->count = timer->count - device_event_queue_time(me);
1436 1.1 christos timer->inhibited = 1;
1437 1.1 christos timer->base_count = reg;
1438 1.1 christos DTRACE(opic, ("timer %d base count register - write 0x%x - timer stopped\n",
1439 1.1 christos index, reg));
1440 1.1 christos }
1441 1.1 christos else {
1442 1.1 christos ASSERT((timer->inhibited && inhibit) || (!timer->inhibited && !inhibit));
1443 1.1 christos DTRACE(opic, ("timer %d base count register - write 0x%x\n", index, reg));
1444 1.1 christos timer->base_count = reg;
1445 1.1 christos }
1446 1.1 christos }
1447 1.1 christos
1448 1.1 christos
1449 1.1 christos static unsigned
1450 1.1 christos do_timer_N_vector_priority_register_read(device *me,
1451 1.1 christos hw_opic_device *opic,
1452 1.1 christos int index)
1453 1.1 christos {
1454 1.1 christos unsigned reg;
1455 1.1 christos ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
1456 1.1 christos reg = read_vector_priority_register(me, opic,
1457 1.1 christos &opic->timer_interrupt_source[index],
1458 1.1 christos "timer", index);
1459 1.1 christos return reg;
1460 1.1 christos }
1461 1.1 christos
1462 1.1 christos static void
1463 1.1 christos do_timer_N_vector_priority_register_write(device *me,
1464 1.1 christos hw_opic_device *opic,
1465 1.1 christos int index,
1466 1.1 christos unsigned reg)
1467 1.1 christos {
1468 1.1 christos ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
1469 1.1 christos reg &= ~isu_level_triggered_bit; /* force edge trigger */
1470 1.1 christos reg |= isu_positive_polarity_bit; /* force rising (positive) edge */
1471 1.1 christos reg |= isu_multicast_bit; /* force multicast */
1472 1.1 christos write_vector_priority_register(me, opic,
1473 1.1 christos &opic->timer_interrupt_source[index],
1474 1.1 christos reg, "timer", index);
1475 1.1 christos }
1476 1.1 christos
1477 1.1 christos
1478 1.1 christos static unsigned
1479 1.1 christos do_timer_N_destination_register_read(device *me,
1480 1.1 christos hw_opic_device *opic,
1481 1.1 christos int index)
1482 1.1 christos {
1483 1.1 christos unsigned reg;
1484 1.1 christos ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
1485 1.1 christos reg = read_destination_register(me, opic, &opic->timer_interrupt_source[index],
1486 1.1 christos "timer", index);
1487 1.1 christos return reg;
1488 1.1 christos }
1489 1.1 christos
1490 1.1 christos static void
1491 1.1 christos do_timer_N_destination_register_write(device *me,
1492 1.1 christos hw_opic_device *opic,
1493 1.1 christos int index,
1494 1.1 christos unsigned reg)
1495 1.1 christos {
1496 1.1 christos ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
1497 1.1 christos write_destination_register(me, opic, &opic->timer_interrupt_source[index],
1498 1.1 christos reg, "timer", index);
1499 1.1 christos }
1500 1.1 christos
1501 1.1 christos
1502 1.1 christos /* IPI registers */
1503 1.1 christos
1504 1.1 christos static unsigned
1505 1.1 christos do_ipi_N_vector_priority_register_read(device *me,
1506 1.1 christos hw_opic_device *opic,
1507 1.1 christos int index)
1508 1.1 christos {
1509 1.1 christos unsigned reg;
1510 1.1 christos ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
1511 1.1 christos reg = read_vector_priority_register(me, opic,
1512 1.1 christos &opic->interprocessor_interrupt_source[index],
1513 1.1 christos "ipi", index);
1514 1.1 christos return reg;
1515 1.1 christos }
1516 1.1 christos
1517 1.1 christos static void
1518 1.1 christos do_ipi_N_vector_priority_register_write(device *me,
1519 1.1 christos hw_opic_device *opic,
1520 1.1 christos int index,
1521 1.1 christos unsigned reg)
1522 1.1 christos {
1523 1.1 christos ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
1524 1.1 christos reg &= ~isu_level_triggered_bit; /* force edge trigger */
1525 1.1 christos reg |= isu_positive_polarity_bit; /* force rising (positive) edge */
1526 1.1 christos reg |= isu_multicast_bit; /* force a multicast source */
1527 1.1 christos write_vector_priority_register(me, opic,
1528 1.1 christos &opic->interprocessor_interrupt_source[index],
1529 1.1 christos reg, "ipi", index);
1530 1.1 christos }
1531 1.1 christos
1532 1.1 christos static void
1533 1.1 christos do_ipi_N_dispatch_register_write(device *me,
1534 1.1 christos hw_opic_device *opic,
1535 1.1 christos int index,
1536 1.1 christos unsigned reg)
1537 1.1 christos {
1538 1.1 christos opic_interrupt_source *source = &opic->interprocessor_interrupt_source[index];
1539 1.1 christos ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
1540 1.1 christos DTRACE(opic, ("ipi %d interrupt dispatch register - write 0x%x\n", index, reg));
1541 1.1 christos source->destination = reg;
1542 1.1 christos handle_interrupt(me, opic, source, 1);
1543 1.1 christos }
1544 1.1 christos
1545 1.1 christos
1546 1.1 christos /* vendor and other global registers */
1547 1.1 christos
1548 1.1 christos static unsigned
1549 1.1 christos do_vendor_identification_register_read(device *me,
1550 1.1 christos hw_opic_device *opic)
1551 1.1 christos {
1552 1.1 christos unsigned reg;
1553 1.1 christos reg = opic->vendor_identification;
1554 1.1 christos DTRACE(opic, ("vendor identification register - read 0x%x\n", reg));
1555 1.1 christos return reg;
1556 1.1 christos }
1557 1.1 christos
1558 1.1 christos static unsigned
1559 1.1 christos do_feature_reporting_register_N_read(device *me,
1560 1.1 christos hw_opic_device *opic,
1561 1.1 christos int index)
1562 1.1 christos {
1563 1.1 christos unsigned reg = 0;
1564 1.1 christos ASSERT(index == 0);
1565 1.1 christos switch (index) {
1566 1.1 christos case 0:
1567 1.1 christos reg |= (opic->nr_external_interrupts << 16);
1568 1.1 christos reg |= (opic->nr_interrupt_destinations << 8);
1569 1.1 christos reg |= (2/*version 1.2*/);
1570 1.1 christos break;
1571 1.1 christos }
1572 1.1 christos DTRACE(opic, ("feature reporting register %d - read 0x%x\n", index, reg));
1573 1.1 christos return reg;
1574 1.1 christos }
1575 1.1 christos
1576 1.1 christos static unsigned
1577 1.1 christos do_global_configuration_register_N_read(device *me,
1578 1.1 christos hw_opic_device *opic,
1579 1.1 christos int index)
1580 1.1 christos {
1581 1.1 christos unsigned reg = 0;
1582 1.1 christos ASSERT(index == 0);
1583 1.1 christos switch (index) {
1584 1.1 christos case 0:
1585 1.1 christos reg |= gcr0_8259_bit; /* hardwire 8259 disabled */
1586 1.1 christos break;
1587 1.1 christos }
1588 1.1 christos DTRACE(opic, ("global configuration register %d - read 0x%x\n", index, reg));
1589 1.1 christos return reg;
1590 1.1 christos }
1591 1.1 christos
1592 1.1 christos static void
1593 1.1 christos do_global_configuration_register_N_write(device *me,
1594 1.1 christos hw_opic_device *opic,
1595 1.1 christos int index,
1596 1.1 christos unsigned reg)
1597 1.1 christos {
1598 1.1 christos ASSERT(index == 0);
1599 1.1 christos if (reg & gcr0_reset_bit) {
1600 1.1 christos DTRACE(opic, ("global configuration register %d - write 0x%x - reseting opic\n", index, reg));
1601 1.1 christos hw_opic_init_data(me);
1602 1.1 christos }
1603 1.1 christos if (!(reg & gcr0_8259_bit)) {
1604 1.1 christos DTRACE(opic, ("global configuration register %d - write 0x%x - ignoring 8259 enable\n", index, reg));
1605 1.1 christos }
1606 1.1 christos }
1607 1.1 christos
1608 1.1 christos
1609 1.1 christos
1610 1.1 christos /* register read-write */
1611 1.1 christos
1612 1.1 christos static unsigned
1613 1.1 christos hw_opic_io_read_buffer(device *me,
1614 1.1 christos void *dest,
1615 1.1 christos int space,
1616 1.1 christos unsigned_word addr,
1617 1.1 christos unsigned nr_bytes,
1618 1.1 christos cpu *processor,
1619 1.1 christos unsigned_word cia)
1620 1.1 christos {
1621 1.1 christos hw_opic_device *opic = (hw_opic_device*)device_data(me);
1622 1.1 christos opic_register type;
1623 1.1 christos int index;
1624 1.1 christos decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index);
1625 1.1 christos if (type == invalid_opic_register) {
1626 1.1 christos device_error(me, "invalid opic read access to %d:0x%lx (%d bytes)",
1627 1.1 christos space, (unsigned long)addr, nr_bytes);
1628 1.1 christos }
1629 1.1 christos else {
1630 1.1 christos unsigned reg;
1631 1.1 christos switch (type) {
1632 1.1 christos case processor_init_register:
1633 1.1 christos reg = do_processor_init_register_read(me, opic);
1634 1.1 christos break;
1635 1.1 christos case interrupt_source_N_vector_priority_register:
1636 1.1 christos reg = do_interrupt_source_N_vector_priority_register_read(me, opic, index);
1637 1.1 christos break;
1638 1.1 christos case interrupt_source_N_destination_register:
1639 1.1 christos reg = do_interrupt_source_N_destination_register_read(me, opic, index);
1640 1.1 christos break;
1641 1.1 christos case interrupt_acknowledge_register_N:
1642 1.1 christos reg = do_interrupt_acknowledge_register_N_read(me, opic, index);
1643 1.1 christos break;
1644 1.1 christos case spurious_vector_register:
1645 1.1 christos reg = do_spurious_vector_register_read(me, opic);
1646 1.1 christos break;
1647 1.1 christos case current_task_priority_register_N:
1648 1.1 christos reg = do_current_task_priority_register_N_read(me, opic, index);
1649 1.1 christos break;
1650 1.1 christos case timer_frequency_reporting_register:
1651 1.1 christos reg = do_timer_frequency_reporting_register_read(me, opic);
1652 1.1 christos break;
1653 1.1 christos case timer_N_current_count_register:
1654 1.1 christos reg = do_timer_N_current_count_register_read(me, opic, index);
1655 1.1 christos break;
1656 1.1 christos case timer_N_base_count_register:
1657 1.1 christos reg = do_timer_N_base_count_register_read(me, opic, index);
1658 1.1 christos break;
1659 1.1 christos case timer_N_vector_priority_register:
1660 1.1 christos reg = do_timer_N_vector_priority_register_read(me, opic, index);
1661 1.1 christos break;
1662 1.1 christos case timer_N_destination_register:
1663 1.1 christos reg = do_timer_N_destination_register_read(me, opic, index);
1664 1.1 christos break;
1665 1.1 christos case ipi_N_vector_priority_register:
1666 1.1 christos reg = do_ipi_N_vector_priority_register_read(me, opic, index);
1667 1.1 christos break;
1668 1.1 christos case feature_reporting_register_N:
1669 1.1 christos reg = do_feature_reporting_register_N_read(me, opic, index);
1670 1.1 christos break;
1671 1.1 christos case global_configuration_register_N:
1672 1.1 christos reg = do_global_configuration_register_N_read(me, opic, index);
1673 1.1 christos break;
1674 1.1 christos case vendor_identification_register:
1675 1.1 christos reg = do_vendor_identification_register_read(me, opic);
1676 1.1 christos break;
1677 1.1 christos default:
1678 1.1 christos reg = 0;
1679 1.1 christos device_error(me, "unimplemented read of register %s[%d]",
1680 1.1 christos opic_register_name(type), index);
1681 1.1 christos }
1682 1.1 christos *(unsigned_4*)dest = H2LE_4(reg);
1683 1.1 christos }
1684 1.1 christos return nr_bytes;
1685 1.1 christos }
1686 1.1 christos
1687 1.1 christos
1688 1.1 christos static unsigned
1689 1.1 christos hw_opic_io_write_buffer(device *me,
1690 1.1 christos const void *source,
1691 1.1 christos int space,
1692 1.1 christos unsigned_word addr,
1693 1.1 christos unsigned nr_bytes,
1694 1.1 christos cpu *processor,
1695 1.1 christos unsigned_word cia)
1696 1.1 christos {
1697 1.1 christos hw_opic_device *opic = (hw_opic_device*)device_data(me);
1698 1.1 christos opic_register type;
1699 1.1 christos int index;
1700 1.1 christos decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index);
1701 1.1 christos if (type == invalid_opic_register) {
1702 1.1 christos device_error(me, "invalid opic write access to %d:0x%lx (%d bytes)",
1703 1.1 christos space, (unsigned long)addr, nr_bytes);
1704 1.1 christos }
1705 1.1 christos else {
1706 1.1 christos unsigned reg = LE2H_4(*(unsigned_4*)source);
1707 1.1 christos switch (type) {
1708 1.1 christos case processor_init_register:
1709 1.1 christos do_processor_init_register_write(me, opic, reg);
1710 1.1 christos break;
1711 1.1 christos case interrupt_source_N_vector_priority_register:
1712 1.1 christos do_interrupt_source_N_vector_priority_register_write(me, opic, index, reg);
1713 1.1 christos break;
1714 1.1 christos case interrupt_source_N_destination_register:
1715 1.1 christos do_interrupt_source_N_destination_register_write(me, opic, index, reg);
1716 1.1 christos break;
1717 1.1 christos case end_of_interrupt_register_N:
1718 1.1 christos do_end_of_interrupt_register_N_write(me, opic, index, reg);
1719 1.1 christos break;
1720 1.1 christos case spurious_vector_register:
1721 1.1 christos do_spurious_vector_register_write(me, opic, reg);
1722 1.1 christos break;
1723 1.1 christos case current_task_priority_register_N:
1724 1.1 christos do_current_task_priority_register_N_write(me, opic, index, reg);
1725 1.1 christos break;
1726 1.1 christos case timer_frequency_reporting_register:
1727 1.1 christos do_timer_frequency_reporting_register_write(me, opic, reg);
1728 1.1 christos break;
1729 1.1 christos case timer_N_base_count_register:
1730 1.1 christos do_timer_N_base_count_register_write(me, opic, index, reg);
1731 1.1 christos break;
1732 1.1 christos case timer_N_vector_priority_register:
1733 1.1 christos do_timer_N_vector_priority_register_write(me, opic, index, reg);
1734 1.1 christos break;
1735 1.1 christos case timer_N_destination_register:
1736 1.1 christos do_timer_N_destination_register_write(me, opic, index, reg);
1737 1.1 christos break;
1738 1.1 christos case ipi_N_dispatch_register:
1739 1.1 christos do_ipi_N_dispatch_register_write(me, opic, index, reg);
1740 1.1 christos break;
1741 1.1 christos case ipi_N_vector_priority_register:
1742 1.1 christos do_ipi_N_vector_priority_register_write(me, opic, index, reg);
1743 1.1 christos break;
1744 1.1 christos case global_configuration_register_N:
1745 1.1 christos do_global_configuration_register_N_write(me, opic, index, reg);
1746 1.1 christos break;
1747 1.1 christos default:
1748 1.1 christos device_error(me, "unimplemented write to register %s[%d]",
1749 1.1 christos opic_register_name(type), index);
1750 1.1 christos }
1751 1.1 christos }
1752 1.1 christos return nr_bytes;
1753 1.1 christos }
1754 1.1 christos
1755 1.1 christos
1756 1.1 christos static void
1757 1.1 christos hw_opic_interrupt_event(device *me,
1758 1.1 christos int my_port,
1759 1.1 christos device *source,
1760 1.1 christos int source_port,
1761 1.1 christos int level,
1762 1.1 christos cpu *processor,
1763 1.1 christos unsigned_word cia)
1764 1.1 christos {
1765 1.1 christos hw_opic_device *opic = (hw_opic_device*)device_data(me);
1766 1.1 christos
1767 1.1 christos int isb;
1768 1.1 christos int src_nr = 0;
1769 1.1 christos
1770 1.1 christos /* find the corresponding internal input port */
1771 1.1 christos for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
1772 1.1 christos if (my_port >= opic->isu_block[isb].int_number
1773 1.1 christos && my_port < opic->isu_block[isb].int_number + opic->isu_block[isb].range) {
1774 1.1 christos src_nr += my_port - opic->isu_block[isb].int_number;
1775 1.1 christos break;
1776 1.1 christos }
1777 1.1 christos else
1778 1.1 christos src_nr += opic->isu_block[isb].range;
1779 1.1 christos }
1780 1.1 christos if (isb == opic->nr_isu_blocks)
1781 1.1 christos device_error(me, "interrupt %d out of range", my_port);
1782 1.1 christos DTRACE(opic, ("external-interrupt %d, internal %d, level %d\n",
1783 1.1 christos my_port, src_nr, level));
1784 1.1 christos
1785 1.1 christos /* pass it on */
1786 1.1 christos ASSERT(src_nr >= 0 && src_nr < opic->nr_external_interrupts);
1787 1.1 christos handle_interrupt(me, opic, &opic->external_interrupt_source[src_nr], level);
1788 1.1 christos }
1789 1.1 christos
1790 1.1 christos
1791 1.1 christos static const device_interrupt_port_descriptor hw_opic_interrupt_ports[] = {
1792 1.1 christos { "irq", 0, max_nr_interrupt_sources, input_port, },
1793 1.1 christos { "intr", 0, max_nr_interrupt_destinations, output_port, },
1794 1.1 christos { "init", max_nr_interrupt_destinations, max_nr_interrupt_destinations, output_port, },
1795 1.1 christos { NULL }
1796 1.1 christos };
1797 1.1 christos
1798 1.1 christos
1799 1.1 christos static device_callbacks const hw_opic_callbacks = {
1800 1.1 christos { generic_device_init_address,
1801 1.1 christos hw_opic_init_data },
1802 1.1 christos { NULL, }, /* address */
1803 1.1 christos { hw_opic_io_read_buffer,
1804 1.1 christos hw_opic_io_write_buffer }, /* IO */
1805 1.1 christos { NULL, }, /* DMA */
1806 1.1 christos { hw_opic_interrupt_event, NULL, hw_opic_interrupt_ports }, /* interrupt */
1807 1.1 christos { NULL, }, /* unit */
1808 1.1 christos NULL, /* instance */
1809 1.1 christos };
1810 1.1 christos
1811 1.1 christos static void *
1812 1.1 christos hw_opic_create(const char *name,
1813 1.1 christos const device_unit *unit_address,
1814 1.1 christos const char *args)
1815 1.1 christos {
1816 1.1 christos hw_opic_device *opic = ZALLOC(hw_opic_device);
1817 1.1 christos return opic;
1818 1.1 christos }
1819 1.1 christos
1820 1.1 christos
1821 1.1 christos
1822 1.1 christos const device_descriptor hw_opic_device_descriptor[] = {
1823 1.1 christos { "opic", hw_opic_create, &hw_opic_callbacks },
1824 1.1 christos { NULL },
1825 1.1 christos };
1826 1.1 christos
1827 1.1 christos #endif /* _HW_OPIC_C_ */
1828