aboutsummaryrefslogtreecommitdiff
path: root/sim/ppc/hw_pic.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/ppc/hw_pic.c')
-rw-r--r--sim/ppc/hw_pic.c98
1 files changed, 98 insertions, 0 deletions
diff --git a/sim/ppc/hw_pic.c b/sim/ppc/hw_pic.c
new file mode 100644
index 0000000..3203f8c
--- /dev/null
+++ b/sim/ppc/hw_pic.c
@@ -0,0 +1,98 @@
+/* ICU device: icu@<address>
+
+ <address> : read - processor nr
+ <address> : write - interrupt processor nr
+ <address> + 4 : read - nr processors
+
+ Single byte registers that control a simple ICU.
+
+ Illustrates passing of events to parent device. Passing of
+ interrupts to an interrupt destination. */
+
+
+static unsigned
+icu_io_read_buffer_callback(device *me,
+ void *dest,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
+{
+ memset(dest, 0, nr_bytes);
+ switch (addr & 4) {
+ case 0:
+ *(unsigned_1*)dest = cpu_nr(processor);
+ break;
+ case 4:
+ *(unsigned_1*)dest =
+ device_find_integer_property(me, "/openprom/options/smp");
+ break;
+ }
+ return nr_bytes;
+}
+
+
+static unsigned
+icu_io_write_buffer_callback(device *me,
+ const void *source,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes,
+ cpu *processor,
+ unsigned_word cia)
+{
+ unsigned_1 val = H2T_1(*(unsigned_1*)source);
+ /* tell the parent device that the interrupt lines have changed.
+ For this fake ICU. The interrupt lines just indicate the cpu to
+ interrupt next */
+ device_interrupt_event(me,
+ val, /*my_port*/
+ val, /*val*/
+ processor, cia);
+ return nr_bytes;
+}
+
+static void
+icu_do_interrupt(event_queue *queue,
+ void *data)
+{
+ cpu *target = (cpu*)data;
+ /* try to interrupt the processor. If the attempt fails, try again
+ on the next tick */
+ if (!external_interrupt(target))
+ event_queue_schedule(queue, 1, icu_do_interrupt, target);
+}
+
+
+static void
+icu_interrupt_event_callback(device *me,
+ int my_port,
+ device *source,
+ int source_port,
+ int level,
+ cpu *processor,
+ unsigned_word cia)
+{
+ /* the interrupt controller can't interrupt a cpu at any time.
+ Rather it must synchronize with the system clock before
+ performing an interrupt on the given processor */
+ psim *system = cpu_system(processor);
+ cpu *target = psim_cpu(system, my_port);
+ if (target != NULL) {
+ event_queue *events = cpu_event_queue(target);
+ event_queue_schedule(events, 1, icu_do_interrupt, target);
+ }
+}
+
+static device_callbacks const icu_callbacks = {
+ { generic_device_init_address, },
+ { NULL, }, /* address */
+ { icu_io_read_buffer_callback,
+ icu_io_write_buffer_callback, },
+ { NULL, }, /* DMA */
+ { icu_interrupt_event_callback, },
+ { NULL, }, /* unit */
+};
+
+