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