OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gdb-7.1/] [sim/] [ppc/] [hw_opic.c] - Diff between revs 834 and 842

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 834 Rev 842
/*  This file is part of the program psim.
/*  This file is part of the program psim.
 
 
    Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
    Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
 
 
    This program is free software; you can redistribute it and/or modify
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
    (at your option) any later version.
 
 
    This program is distributed in the hope that it will be useful,
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    GNU General Public License for more details.
 
 
    You should have received a copy of the GNU General Public License
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 
    */
    */
 
 
 
 
#ifndef _HW_OPIC_C_
#ifndef _HW_OPIC_C_
#define _HW_OPIC_C_
#define _HW_OPIC_C_
 
 
#include "device_table.h"
#include "device_table.h"
 
 
#ifdef HAVE_STRING_H
#ifdef HAVE_STRING_H
#include <string.h>
#include <string.h>
#else
#else
#ifdef HAVE_STRINGS_H
#ifdef HAVE_STRINGS_H
#include <strings.h>
#include <strings.h>
#endif
#endif
#endif
#endif
 
 
 
 
/* DEVICE
/* DEVICE
 
 
 
 
   opic - Open Programmable Interrupt Controller (OpenPIC)
   opic - Open Programmable Interrupt Controller (OpenPIC)
 
 
 
 
   DESCRIPTION
   DESCRIPTION
 
 
 
 
   This device implements the core of the OpenPIC interrupt controller
   This device implements the core of the OpenPIC interrupt controller
   as described in the OpenPIC specification 1.2 and other related
   as described in the OpenPIC specification 1.2 and other related
   documents.
   documents.
 
 
   The model includes:
   The model includes:
 
 
   o    Up to 2048 external interrupt sources
   o    Up to 2048 external interrupt sources
 
 
   o    The four count down timers
   o    The four count down timers
 
 
   o    The four interprocessor multicast interrupts
   o    The four interprocessor multicast interrupts
 
 
   o    multiprocessor support
   o    multiprocessor support
 
 
   o    Full tracing to assist help debugging
   o    Full tracing to assist help debugging
 
 
   o    Support for all variations of edge/level x high/low polarity.
   o    Support for all variations of edge/level x high/low polarity.
 
 
 
 
 
 
   PROPERTIES
   PROPERTIES
 
 
 
 
   reg = <address> <size> ... (required)
   reg = <address> <size> ... (required)
 
 
   Determine where the device lives in the parents address space.  The
   Determine where the device lives in the parents address space.  The
   first <<address>> <<size>> pair specifies the address of the
   first <<address>> <<size>> pair specifies the address of the
   interrupt destination unit (which might contain an interrupt source
   interrupt destination unit (which might contain an interrupt source
   unit) while successive reg entries specify additional interrupt
   unit) while successive reg entries specify additional interrupt
   source units.
   source units.
 
 
   Note that for an <<opic>> device attached to a <<pci>> bus, the
   Note that for an <<opic>> device attached to a <<pci>> bus, the
   first <<reg>> entry may need to be ignored it will be the address
   first <<reg>> entry may need to be ignored it will be the address
   of the devices configuration registers.
   of the devices configuration registers.
 
 
 
 
   interrupt-ranges = <int-number> <range> ... (required)
   interrupt-ranges = <int-number> <range> ... (required)
 
 
   A list of pairs.  Each pair corresponds to a block of interrupt
   A list of pairs.  Each pair corresponds to a block of interrupt
   source units (the address of which being specified by the
   source units (the address of which being specified by the
   corresponding reg tupple).  <<int-number>> is the number of the
   corresponding reg tupple).  <<int-number>> is the number of the
   first interrupt in the block while <<range>> is the number of
   first interrupt in the block while <<range>> is the number of
   interrupts in the block.
   interrupts in the block.
 
 
 
 
   timer-frequency = <integer>  (optional)
   timer-frequency = <integer>  (optional)
 
 
   If present, specifies the default value of the timer frequency
   If present, specifies the default value of the timer frequency
   reporting register.  By default a value of 1 HZ is used.  The value
   reporting register.  By default a value of 1 HZ is used.  The value
   is arbitrary, the timers are always updated once per machine cycle.
   is arbitrary, the timers are always updated once per machine cycle.
 
 
 
 
   vendor-identification = <integer>  (optional)
   vendor-identification = <integer>  (optional)
 
 
   If present, specifies the value to be returned when the vendor
   If present, specifies the value to be returned when the vendor
   identification register is read.
   identification register is read.
 
 
 
 
   EXAMPLES
   EXAMPLES
 
 
 
 
   See the test suite directory:
   See the test suite directory:
 
 
   |  psim-test/hw-opic
   |  psim-test/hw-opic
 
 
 
 
   BUGS
   BUGS
 
 
   For an OPIC controller attached to a PCI bus, it is not clear what
   For an OPIC controller attached to a PCI bus, it is not clear what
   the value of the <<reg>> and <<interrupt-ranges>> properties should
   the value of the <<reg>> and <<interrupt-ranges>> properties should
   be.  In particular, the PCI firmware bindings require the first
   be.  In particular, the PCI firmware bindings require the first
   value of the <<reg>> property to specify the devices configuration
   value of the <<reg>> property to specify the devices configuration
   address while the OpenPIC bindings require that same entry to
   address while the OpenPIC bindings require that same entry to
   specify the address of the Interrupt Delivery Unit.  This
   specify the address of the Interrupt Delivery Unit.  This
   implementation checks for and, if present, ignores any
   implementation checks for and, if present, ignores any
   configuration address (and its corresponding <<interrupt-ranges>>
   configuration address (and its corresponding <<interrupt-ranges>>
   entry).
   entry).
 
 
   The OpenPIC specification requires the controller to be fair when
   The OpenPIC specification requires the controller to be fair when
   distributing interrupts between processors.  At present the
   distributing interrupts between processors.  At present the
   algorithm used isn't fair.  It is biased towards processor zero.
   algorithm used isn't fair.  It is biased towards processor zero.
 
 
   The OpenPIC specification includes a 8259 pass through mode.  This
   The OpenPIC specification includes a 8259 pass through mode.  This
   is not supported.
   is not supported.
 
 
 
 
   REFERENCES
   REFERENCES
 
 
 
 
   PowerPC Multiprocessor Interrupt Controller (MPIC), January 19,
   PowerPC Multiprocessor Interrupt Controller (MPIC), January 19,
   1996. Available from IBM.
   1996. Available from IBM.
 
 
 
 
   The Open Programmable Interrupt Controller (PIC) Register Interface
   The Open Programmable Interrupt Controller (PIC) Register Interface
   Specification Revision 1.2.  Issue Date: Opctober 1995.  Available
   Specification Revision 1.2.  Issue Date: Opctober 1995.  Available
   somewhere on AMD's web page (http://www.amd.com/)
   somewhere on AMD's web page (http://www.amd.com/)
 
 
 
 
   PowerPC Microprocessor Common Hardware Reference Platform (CHRP)
   PowerPC Microprocessor Common Hardware Reference Platform (CHRP)
   System bindings to: IEEE Std 1275-1994 Standard for Boot
   System bindings to: IEEE Std 1275-1994 Standard for Boot
   (Initialization, Configuration) Firmware.  Revision 1.2b (INTERIM
   (Initialization, Configuration) Firmware.  Revision 1.2b (INTERIM
   DRAFT).  April 22, 1996.  Available on the Open Firmware web site
   DRAFT).  April 22, 1996.  Available on the Open Firmware web site
   http://playground.sun.com/p1275/.
   http://playground.sun.com/p1275/.
 
 
 
 
   */
   */
 
 
 
 
/* forward types */
/* forward types */
 
 
typedef struct _hw_opic_device hw_opic_device;
typedef struct _hw_opic_device hw_opic_device;
 
 
 
 
/* bounds */
/* bounds */
 
 
enum {
enum {
  max_nr_interrupt_sources = 2048,
  max_nr_interrupt_sources = 2048,
  max_nr_interrupt_destinations = 32,
  max_nr_interrupt_destinations = 32,
  max_nr_task_priorities = 16,
  max_nr_task_priorities = 16,
};
};
 
 
 
 
enum {
enum {
  opic_alignment = 16,
  opic_alignment = 16,
};
};
 
 
 
 
/* global configuration register */
/* global configuration register */
 
 
enum {
enum {
  gcr0_8259_bit = 0x20000000,
  gcr0_8259_bit = 0x20000000,
  gcr0_reset_bit = 0x80000000,
  gcr0_reset_bit = 0x80000000,
};
};
 
 
 
 
/* offsets and sizes */
/* offsets and sizes */
 
 
enum {
enum {
  idu_isu_base = 0x10000,
  idu_isu_base = 0x10000,
  sizeof_isu_register_block = 32,
  sizeof_isu_register_block = 32,
  idu_per_processor_register_base = 0x20000,
  idu_per_processor_register_base = 0x20000,
  sizeof_idu_per_processor_register_block = 0x1000,
  sizeof_idu_per_processor_register_block = 0x1000,
  idu_timer_base = 0x01100,
  idu_timer_base = 0x01100,
  sizeof_timer_register_block = 0x00040,
  sizeof_timer_register_block = 0x00040,
};
};
 
 
 
 
/* Interrupt sources */
/* Interrupt sources */
 
 
enum {
enum {
  isu_mask_bit = 0x80000000,
  isu_mask_bit = 0x80000000,
  isu_active_bit = 0x40000000,
  isu_active_bit = 0x40000000,
  isu_multicast_bit = 0x20000000,
  isu_multicast_bit = 0x20000000,
  isu_positive_polarity_bit = 0x00800000,
  isu_positive_polarity_bit = 0x00800000,
  isu_level_triggered_bit = 0x00400000,
  isu_level_triggered_bit = 0x00400000,
  isu_priority_shift = 16,
  isu_priority_shift = 16,
  isu_vector_bits = 0x000000ff,
  isu_vector_bits = 0x000000ff,
};
};
 
 
 
 
typedef struct _opic_interrupt_source {
typedef struct _opic_interrupt_source {
  unsigned is_masked; /* left in place */
  unsigned is_masked; /* left in place */
  unsigned is_multicast; /* left in place */
  unsigned is_multicast; /* left in place */
  unsigned is_positive_polarity; /* left in place */
  unsigned is_positive_polarity; /* left in place */
  unsigned is_level_triggered; /* left in place */
  unsigned is_level_triggered; /* left in place */
  unsigned priority;
  unsigned priority;
  unsigned vector;
  unsigned vector;
  /* misc */
  /* misc */
  int nr;
  int nr;
  unsigned destination;
  unsigned destination;
  unsigned pending;
  unsigned pending;
  unsigned in_service;
  unsigned in_service;
} opic_interrupt_source;
} opic_interrupt_source;
 
 
 
 
/* interrupt destinations (normally processors) */
/* interrupt destinations (normally processors) */
 
 
typedef struct _opic_interrupt_destination {
typedef struct _opic_interrupt_destination {
  int nr;
  int nr;
  unsigned base_priority;
  unsigned base_priority;
  opic_interrupt_source *current_pending;
  opic_interrupt_source *current_pending;
  opic_interrupt_source *current_in_service;
  opic_interrupt_source *current_in_service;
  unsigned bit;
  unsigned bit;
  int init_port;
  int init_port;
  int intr_port;
  int intr_port;
} opic_interrupt_destination;
} opic_interrupt_destination;
 
 
 
 
/* address map descriptors */
/* address map descriptors */
 
 
typedef struct _opic_isu_block { /* interrupt source unit block */
typedef struct _opic_isu_block { /* interrupt source unit block */
  int space;
  int space;
  unsigned_word address;
  unsigned_word address;
  unsigned size;
  unsigned size;
  unsigned_cell int_number;
  unsigned_cell int_number;
  unsigned_cell range;
  unsigned_cell range;
  int reg;
  int reg;
} opic_isu_block;
} opic_isu_block;
 
 
 
 
typedef struct _opic_idu { /* interrupt delivery unit */
typedef struct _opic_idu { /* interrupt delivery unit */
  int reg;
  int reg;
  int space;
  int space;
  unsigned_word address;
  unsigned_word address;
  unsigned size;
  unsigned size;
} opic_idu;
} opic_idu;
 
 
typedef enum {
typedef enum {
  /* bad */
  /* bad */
  invalid_opic_register,
  invalid_opic_register,
  /* interrupt source */
  /* interrupt source */
  interrupt_source_N_destination_register,
  interrupt_source_N_destination_register,
  interrupt_source_N_vector_priority_register,
  interrupt_source_N_vector_priority_register,
  /* timers */
  /* timers */
  timer_N_destination_register,
  timer_N_destination_register,
  timer_N_vector_priority_register,
  timer_N_vector_priority_register,
  timer_N_base_count_register,
  timer_N_base_count_register,
  timer_N_current_count_register,
  timer_N_current_count_register,
  timer_frequency_reporting_register,
  timer_frequency_reporting_register,
  /* inter-processor interrupts */
  /* inter-processor interrupts */
  ipi_N_vector_priority_register,
  ipi_N_vector_priority_register,
  ipi_N_dispatch_register,
  ipi_N_dispatch_register,
  /* global configuration */
  /* global configuration */
  spurious_vector_register,
  spurious_vector_register,
  processor_init_register,
  processor_init_register,
  vendor_identification_register,
  vendor_identification_register,
  global_configuration_register_N,
  global_configuration_register_N,
  feature_reporting_register_N,
  feature_reporting_register_N,
  /* per processor */
  /* per processor */
  end_of_interrupt_register_N,
  end_of_interrupt_register_N,
  interrupt_acknowledge_register_N,
  interrupt_acknowledge_register_N,
  current_task_priority_register_N,
  current_task_priority_register_N,
} opic_register;
} opic_register;
 
 
static const char *
static const char *
opic_register_name(opic_register type)
opic_register_name(opic_register type)
{
{
  switch (type) {
  switch (type) {
  case invalid_opic_register: return "invalid_opic_register";
  case invalid_opic_register: return "invalid_opic_register";
  case interrupt_source_N_destination_register: return "interrupt_source_N_destination_register";
  case interrupt_source_N_destination_register: return "interrupt_source_N_destination_register";
  case interrupt_source_N_vector_priority_register: return "interrupt_source_N_vector_priority_register";
  case interrupt_source_N_vector_priority_register: return "interrupt_source_N_vector_priority_register";
  case timer_N_destination_register: return "timer_N_destination_register";
  case timer_N_destination_register: return "timer_N_destination_register";
  case timer_N_vector_priority_register: return "timer_N_vector_priority_register";
  case timer_N_vector_priority_register: return "timer_N_vector_priority_register";
  case timer_N_base_count_register: return "timer_N_base_count_register";
  case timer_N_base_count_register: return "timer_N_base_count_register";
  case timer_N_current_count_register: return "timer_N_current_count_register";
  case timer_N_current_count_register: return "timer_N_current_count_register";
  case timer_frequency_reporting_register: return "timer_frequency_reporting_register";
  case timer_frequency_reporting_register: return "timer_frequency_reporting_register";
  case ipi_N_vector_priority_register: return "ipi_N_vector_priority_register";
  case ipi_N_vector_priority_register: return "ipi_N_vector_priority_register";
  case ipi_N_dispatch_register: return "ipi_N_dispatch_register";
  case ipi_N_dispatch_register: return "ipi_N_dispatch_register";
  case spurious_vector_register: return "spurious_vector_register";
  case spurious_vector_register: return "spurious_vector_register";
  case processor_init_register: return "processor_init_register";
  case processor_init_register: return "processor_init_register";
  case vendor_identification_register: return "vendor_identification_register";
  case vendor_identification_register: return "vendor_identification_register";
  case global_configuration_register_N: return "global_configuration_register_N";
  case global_configuration_register_N: return "global_configuration_register_N";
  case feature_reporting_register_N: return "feature_reporting_register_N";
  case feature_reporting_register_N: return "feature_reporting_register_N";
  case end_of_interrupt_register_N: return "end_of_interrupt_register_N";
  case end_of_interrupt_register_N: return "end_of_interrupt_register_N";
  case interrupt_acknowledge_register_N: return "interrupt_acknowledge_register_N";
  case interrupt_acknowledge_register_N: return "interrupt_acknowledge_register_N";
  case current_task_priority_register_N: return "current_task_priority_register_N";
  case current_task_priority_register_N: return "current_task_priority_register_N";
  }
  }
  return NULL;
  return NULL;
}
}
 
 
 
 
 
 
/* timers */
/* timers */
 
 
typedef struct _opic_timer {
typedef struct _opic_timer {
  int nr;
  int nr;
  device *me; /* find my way home */
  device *me; /* find my way home */
  hw_opic_device *opic; /* ditto */
  hw_opic_device *opic; /* ditto */
  unsigned base_count;
  unsigned base_count;
  int inhibited;
  int inhibited;
  signed64 count; /* *ONLY* if inhibited */
  signed64 count; /* *ONLY* if inhibited */
  event_entry_tag timeout_event;
  event_entry_tag timeout_event;
  opic_interrupt_source *interrupt_source;
  opic_interrupt_source *interrupt_source;
} opic_timer;
} opic_timer;
 
 
 
 
/* the OPIC */
/* the OPIC */
 
 
struct _hw_opic_device {
struct _hw_opic_device {
 
 
  /* vendor id */
  /* vendor id */
  unsigned vendor_identification;
  unsigned vendor_identification;
 
 
  /* interrupt destinations - processors */
  /* interrupt destinations - processors */
  int nr_interrupt_destinations;
  int nr_interrupt_destinations;
  opic_interrupt_destination *interrupt_destination;
  opic_interrupt_destination *interrupt_destination;
  unsigned sizeof_interrupt_destination;
  unsigned sizeof_interrupt_destination;
 
 
  /* bogus interrupts */
  /* bogus interrupts */
  int spurious_vector;
  int spurious_vector;
 
 
  /* interrupt sources - external interrupt source units + extra internal ones */
  /* interrupt sources - external interrupt source units + extra internal ones */
  int nr_interrupt_sources;
  int nr_interrupt_sources;
  opic_interrupt_source *interrupt_source;
  opic_interrupt_source *interrupt_source;
  unsigned sizeof_interrupt_source;
  unsigned sizeof_interrupt_source;
 
 
  /* external interrupts */
  /* external interrupts */
  int nr_external_interrupts;
  int nr_external_interrupts;
  opic_interrupt_source *external_interrupt_source;
  opic_interrupt_source *external_interrupt_source;
 
 
  /* inter-processor-interrupts */
  /* inter-processor-interrupts */
  int nr_interprocessor_interrupts;
  int nr_interprocessor_interrupts;
  opic_interrupt_source *interprocessor_interrupt_source;
  opic_interrupt_source *interprocessor_interrupt_source;
 
 
  /* timers */
  /* timers */
  int nr_timer_interrupts;
  int nr_timer_interrupts;
  opic_timer *timer;
  opic_timer *timer;
  unsigned sizeof_timer;
  unsigned sizeof_timer;
  opic_interrupt_source *timer_interrupt_source;
  opic_interrupt_source *timer_interrupt_source;
  unsigned timer_frequency;
  unsigned timer_frequency;
 
 
  /* init register */
  /* init register */
  unsigned32 init;
  unsigned32 init;
 
 
  /* address maps */
  /* address maps */
  opic_idu idu;
  opic_idu idu;
  int nr_isu_blocks;
  int nr_isu_blocks;
  opic_isu_block *isu_block;
  opic_isu_block *isu_block;
};
};
 
 
 
 
static void
static void
hw_opic_init_data(device *me)
hw_opic_init_data(device *me)
{
{
  hw_opic_device *opic = (hw_opic_device*)device_data(me);
  hw_opic_device *opic = (hw_opic_device*)device_data(me);
  int isb;
  int isb;
  int idu_reg;
  int idu_reg;
  int nr_isu_blocks;
  int nr_isu_blocks;
  int i;
  int i;
 
 
  /* determine the first valid reg property entry (there could be
  /* determine the first valid reg property entry (there could be
     leading reg entries with invalid (zero) size fields) and the
     leading reg entries with invalid (zero) size fields) and the
     number of isu entries found in the reg property. */
     number of isu entries found in the reg property. */
  idu_reg = 0;
  idu_reg = 0;
  nr_isu_blocks = 0;
  nr_isu_blocks = 0;
  while (1) {
  while (1) {
    reg_property_spec unit;
    reg_property_spec unit;
    int attach_space;
    int attach_space;
    unsigned_word attach_address;
    unsigned_word attach_address;
    unsigned attach_size;
    unsigned attach_size;
    if (!device_find_reg_array_property(me, "reg", idu_reg + nr_isu_blocks,
    if (!device_find_reg_array_property(me, "reg", idu_reg + nr_isu_blocks,
                                        &unit))
                                        &unit))
      break;
      break;
    if (nr_isu_blocks > 0
    if (nr_isu_blocks > 0
        || (device_address_to_attach_address(device_parent(me), &unit.address,
        || (device_address_to_attach_address(device_parent(me), &unit.address,
                                             &attach_space, &attach_address,
                                             &attach_space, &attach_address,
                                             me)
                                             me)
            && device_size_to_attach_size(device_parent(me), &unit.size,
            && device_size_to_attach_size(device_parent(me), &unit.size,
                                          &attach_size,
                                          &attach_size,
                                          me))) {
                                          me))) {
      /* we count any thing once we've found one valid address/size pair */
      /* we count any thing once we've found one valid address/size pair */
      nr_isu_blocks += 1;
      nr_isu_blocks += 1;
    }
    }
    else {
    else {
      idu_reg += 1;
      idu_reg += 1;
    }
    }
  }
  }
 
 
  /* determine the number and location of the multiple interrupt
  /* determine the number and location of the multiple interrupt
     source units and the single interrupt delivery unit */
     source units and the single interrupt delivery unit */
  if (opic->isu_block == NULL) {
  if (opic->isu_block == NULL) {
    int reg_nr;
    int reg_nr;
    opic->nr_isu_blocks = nr_isu_blocks;
    opic->nr_isu_blocks = nr_isu_blocks;
    opic->isu_block = zalloc(sizeof(opic_isu_block) * opic->nr_isu_blocks);
    opic->isu_block = zalloc(sizeof(opic_isu_block) * opic->nr_isu_blocks);
    isb = 0;
    isb = 0;
    reg_nr = idu_reg;
    reg_nr = idu_reg;
    while (isb < opic->nr_isu_blocks) {
    while (isb < opic->nr_isu_blocks) {
      reg_property_spec reg;
      reg_property_spec reg;
      if (!device_find_reg_array_property(me, "reg", reg_nr, &reg))
      if (!device_find_reg_array_property(me, "reg", reg_nr, &reg))
        device_error(me, "reg property missing entry number %d", reg_nr);
        device_error(me, "reg property missing entry number %d", reg_nr);
      opic->isu_block[isb].reg = reg_nr;
      opic->isu_block[isb].reg = reg_nr;
      if (!device_address_to_attach_address(device_parent(me), &reg.address,
      if (!device_address_to_attach_address(device_parent(me), &reg.address,
                                            &opic->isu_block[isb].space,
                                            &opic->isu_block[isb].space,
                                            &opic->isu_block[isb].address,
                                            &opic->isu_block[isb].address,
                                            me)
                                            me)
          || !device_size_to_attach_size(device_parent(me), &reg.size,
          || !device_size_to_attach_size(device_parent(me), &reg.size,
                                         &opic->isu_block[isb].size,
                                         &opic->isu_block[isb].size,
                                         me)) {
                                         me)) {
        device_error(me, "reg property entry %d invalid", reg_nr);
        device_error(me, "reg property entry %d invalid", reg_nr);
      }
      }
      if (!device_find_integer_array_property(me, "interrupt-ranges",
      if (!device_find_integer_array_property(me, "interrupt-ranges",
                                              reg_nr * 2,
                                              reg_nr * 2,
                                              &opic->isu_block[isb].int_number)
                                              &opic->isu_block[isb].int_number)
          || !device_find_integer_array_property(me, "interrupt-ranges",
          || !device_find_integer_array_property(me, "interrupt-ranges",
                                                 reg_nr * 2 + 1,
                                                 reg_nr * 2 + 1,
                                                 &opic->isu_block[isb].range))
                                                 &opic->isu_block[isb].range))
        device_error(me, "missing or invalid interrupt-ranges property entry %d", reg_nr);
        device_error(me, "missing or invalid interrupt-ranges property entry %d", reg_nr);
      /* first reg entry specifies the address of both the IDU and the
      /* first reg entry specifies the address of both the IDU and the
         first set of ISU registers, adjust things accordingly */
         first set of ISU registers, adjust things accordingly */
      if (reg_nr == idu_reg) {
      if (reg_nr == idu_reg) {
        opic->idu.reg = opic->isu_block[isb].reg;
        opic->idu.reg = opic->isu_block[isb].reg;
        opic->idu.space = opic->isu_block[isb].space;
        opic->idu.space = opic->isu_block[isb].space;
        opic->idu.address = opic->isu_block[isb].address;
        opic->idu.address = opic->isu_block[isb].address;
        opic->idu.size = opic->isu_block[isb].size;
        opic->idu.size = opic->isu_block[isb].size;
        opic->isu_block[isb].address += idu_isu_base;
        opic->isu_block[isb].address += idu_isu_base;
        opic->isu_block[isb].size = opic->isu_block[isb].range * (16 + 16);
        opic->isu_block[isb].size = opic->isu_block[isb].range * (16 + 16);
      }
      }
      /* was this a valid reg entry? */
      /* was this a valid reg entry? */
      if (opic->isu_block[isb].range == 0) {
      if (opic->isu_block[isb].range == 0) {
        opic->nr_isu_blocks -= 1;
        opic->nr_isu_blocks -= 1;
      }
      }
      else {
      else {
        opic->nr_external_interrupts += opic->isu_block[isb].range;
        opic->nr_external_interrupts += opic->isu_block[isb].range;
        isb++;
        isb++;
      }
      }
      reg_nr++;
      reg_nr++;
    }
    }
  }
  }
  DTRACE(opic, ("interrupt source unit block - effective number of blocks %d\n",
  DTRACE(opic, ("interrupt source unit block - effective number of blocks %d\n",
                (int)opic->nr_isu_blocks));
                (int)opic->nr_isu_blocks));
 
 
 
 
  /* the number of other interrupts */
  /* the number of other interrupts */
  opic->nr_interprocessor_interrupts = 4;
  opic->nr_interprocessor_interrupts = 4;
  opic->nr_timer_interrupts = 4;
  opic->nr_timer_interrupts = 4;
 
 
 
 
  /* create space for the interrupt source registers */
  /* create space for the interrupt source registers */
  if (opic->interrupt_source != NULL) {
  if (opic->interrupt_source != NULL) {
    memset(opic->interrupt_source, 0, opic->sizeof_interrupt_source);
    memset(opic->interrupt_source, 0, opic->sizeof_interrupt_source);
  }
  }
  else {
  else {
    opic->nr_interrupt_sources = (opic->nr_external_interrupts
    opic->nr_interrupt_sources = (opic->nr_external_interrupts
                                  + opic->nr_interprocessor_interrupts
                                  + opic->nr_interprocessor_interrupts
                                  + opic->nr_timer_interrupts);
                                  + opic->nr_timer_interrupts);
    if (opic->nr_interrupt_sources > max_nr_interrupt_sources)
    if (opic->nr_interrupt_sources > max_nr_interrupt_sources)
      device_error(me, "number of interrupt sources exceeded");
      device_error(me, "number of interrupt sources exceeded");
    opic->sizeof_interrupt_source = (sizeof(opic_interrupt_source)
    opic->sizeof_interrupt_source = (sizeof(opic_interrupt_source)
                                     * opic->nr_interrupt_sources);
                                     * opic->nr_interrupt_sources);
    opic->interrupt_source = zalloc(opic->sizeof_interrupt_source);
    opic->interrupt_source = zalloc(opic->sizeof_interrupt_source);
    opic->external_interrupt_source = opic->interrupt_source;
    opic->external_interrupt_source = opic->interrupt_source;
    opic->interprocessor_interrupt_source = (opic->external_interrupt_source
    opic->interprocessor_interrupt_source = (opic->external_interrupt_source
                                             + opic->nr_external_interrupts);
                                             + opic->nr_external_interrupts);
    opic->timer_interrupt_source = (opic->interprocessor_interrupt_source
    opic->timer_interrupt_source = (opic->interprocessor_interrupt_source
                                    + opic->nr_interprocessor_interrupts);
                                    + opic->nr_interprocessor_interrupts);
  }
  }
  for (i = 0; i < opic->nr_interrupt_sources; i++) {
  for (i = 0; i < opic->nr_interrupt_sources; i++) {
    opic_interrupt_source *source = &opic->interrupt_source[i];
    opic_interrupt_source *source = &opic->interrupt_source[i];
    source->nr = i;
    source->nr = i;
    source->is_masked = isu_mask_bit;
    source->is_masked = isu_mask_bit;
  }
  }
  DTRACE(opic, ("interrupt sources - external %d, timer %d, ipi %d, total %d\n",
  DTRACE(opic, ("interrupt sources - external %d, timer %d, ipi %d, total %d\n",
                opic->nr_external_interrupts,
                opic->nr_external_interrupts,
                opic->nr_timer_interrupts,
                opic->nr_timer_interrupts,
                opic->nr_interprocessor_interrupts,
                opic->nr_interprocessor_interrupts,
                opic->nr_interrupt_sources));
                opic->nr_interrupt_sources));
 
 
 
 
  /* timers or interprocessor interrupts */
  /* timers or interprocessor interrupts */
  if (opic->timer != NULL)
  if (opic->timer != NULL)
    memset(opic->timer, 0, opic->sizeof_timer);
    memset(opic->timer, 0, opic->sizeof_timer);
  else {
  else {
    opic->nr_timer_interrupts = 4;
    opic->nr_timer_interrupts = 4;
    opic->sizeof_timer = sizeof(opic_timer) * opic->nr_timer_interrupts;
    opic->sizeof_timer = sizeof(opic_timer) * opic->nr_timer_interrupts;
    opic->timer = zalloc(opic->sizeof_timer);
    opic->timer = zalloc(opic->sizeof_timer);
  }
  }
  for (i = 0; i < opic->nr_timer_interrupts; i++) {
  for (i = 0; i < opic->nr_timer_interrupts; i++) {
    opic_timer *timer = &opic->timer[i];
    opic_timer *timer = &opic->timer[i];
    timer->nr = i;
    timer->nr = i;
    timer->me = me;
    timer->me = me;
    timer->opic = opic;
    timer->opic = opic;
    timer->inhibited = 1;
    timer->inhibited = 1;
    timer->interrupt_source = &opic->timer_interrupt_source[i];
    timer->interrupt_source = &opic->timer_interrupt_source[i];
  }
  }
  if (device_find_property(me, "timer-frequency"))
  if (device_find_property(me, "timer-frequency"))
    opic->timer_frequency = device_find_integer_property(me, "timer-frequency");
    opic->timer_frequency = device_find_integer_property(me, "timer-frequency");
  else
  else
    opic->timer_frequency = 1;
    opic->timer_frequency = 1;
 
 
 
 
  /* create space for the interrupt destination registers */
  /* create space for the interrupt destination registers */
  if (opic->interrupt_destination != NULL) {
  if (opic->interrupt_destination != NULL) {
    memset(opic->interrupt_destination, 0, opic->sizeof_interrupt_destination);
    memset(opic->interrupt_destination, 0, opic->sizeof_interrupt_destination);
  }
  }
  else {
  else {
    opic->nr_interrupt_destinations = tree_find_integer_property(me, "/openprom/options/smp");
    opic->nr_interrupt_destinations = tree_find_integer_property(me, "/openprom/options/smp");
    opic->sizeof_interrupt_destination = (sizeof(opic_interrupt_destination)
    opic->sizeof_interrupt_destination = (sizeof(opic_interrupt_destination)
                                          * opic->nr_interrupt_destinations);
                                          * opic->nr_interrupt_destinations);
    opic->interrupt_destination = zalloc(opic->sizeof_interrupt_destination);
    opic->interrupt_destination = zalloc(opic->sizeof_interrupt_destination);
    if (opic->nr_interrupt_destinations > max_nr_interrupt_destinations)
    if (opic->nr_interrupt_destinations > max_nr_interrupt_destinations)
      device_error(me, "number of interrupt destinations exceeded");
      device_error(me, "number of interrupt destinations exceeded");
  }
  }
  for (i = 0; i < opic->nr_interrupt_destinations; i++) {
  for (i = 0; i < opic->nr_interrupt_destinations; i++) {
    opic_interrupt_destination *dest = &opic->interrupt_destination[i];
    opic_interrupt_destination *dest = &opic->interrupt_destination[i];
    dest->bit = (1 << i);
    dest->bit = (1 << i);
    dest->nr = i;
    dest->nr = i;
    dest->init_port = (device_interrupt_decode(me, "init0", output_port)
    dest->init_port = (device_interrupt_decode(me, "init0", output_port)
                       + i);
                       + i);
    dest->intr_port = (device_interrupt_decode(me, "intr0", output_port)
    dest->intr_port = (device_interrupt_decode(me, "intr0", output_port)
                       + i);
                       + i);
    dest->base_priority = max_nr_task_priorities - 1;
    dest->base_priority = max_nr_task_priorities - 1;
  }
  }
  DTRACE(opic, ("interrupt destinations - total %d\n",
  DTRACE(opic, ("interrupt destinations - total %d\n",
                (int)opic->nr_interrupt_destinations));
                (int)opic->nr_interrupt_destinations));
 
 
 
 
  /* verify and print out the ISU's */
  /* verify and print out the ISU's */
  for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
  for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
    unsigned correct_size;
    unsigned correct_size;
    if ((opic->isu_block[isb].address % opic_alignment) != 0)
    if ((opic->isu_block[isb].address % opic_alignment) != 0)
      device_error(me, "interrupt source unit %d address not aligned to %d byte boundary",
      device_error(me, "interrupt source unit %d address not aligned to %d byte boundary",
                   isb, opic_alignment);
                   isb, opic_alignment);
    correct_size = opic->isu_block[isb].range * sizeof_isu_register_block;
    correct_size = opic->isu_block[isb].range * sizeof_isu_register_block;
    if (opic->isu_block[isb].size != correct_size)
    if (opic->isu_block[isb].size != correct_size)
      device_error(me, "interrupt source unit %d (reg %d) has an incorrect size, should be 0x%x",
      device_error(me, "interrupt source unit %d (reg %d) has an incorrect size, should be 0x%x",
                   isb, opic->isu_block[isb].reg, correct_size);
                   isb, opic->isu_block[isb].reg, correct_size);
    DTRACE(opic, ("interrupt source unit block %ld - address %d:0x%lx, size 0x%lx, int-number %ld, range %ld\n",
    DTRACE(opic, ("interrupt source unit block %ld - address %d:0x%lx, size 0x%lx, int-number %ld, range %ld\n",
                  (long)isb,
                  (long)isb,
                  (int)opic->isu_block[isb].space,
                  (int)opic->isu_block[isb].space,
                  (unsigned long)opic->isu_block[isb].address,
                  (unsigned long)opic->isu_block[isb].address,
                  (unsigned long)opic->isu_block[isb].size,
                  (unsigned long)opic->isu_block[isb].size,
                  (long)opic->isu_block[isb].int_number,
                  (long)opic->isu_block[isb].int_number,
                  (long)opic->isu_block[isb].range));
                  (long)opic->isu_block[isb].range));
  }
  }
 
 
 
 
  /* verify and print out the IDU */
  /* verify and print out the IDU */
  {
  {
    unsigned correct_size;
    unsigned correct_size;
    unsigned alternate_size;
    unsigned alternate_size;
    if ((opic->idu.address % opic_alignment) != 0)
    if ((opic->idu.address % opic_alignment) != 0)
      device_error(me, "interrupt delivery unit not aligned to %d byte boundary",
      device_error(me, "interrupt delivery unit not aligned to %d byte boundary",
                   opic_alignment);
                   opic_alignment);
    correct_size = (idu_per_processor_register_base
    correct_size = (idu_per_processor_register_base
                    + (sizeof_idu_per_processor_register_block
                    + (sizeof_idu_per_processor_register_block
                       * opic->nr_interrupt_destinations));
                       * opic->nr_interrupt_destinations));
    alternate_size = (idu_per_processor_register_base
    alternate_size = (idu_per_processor_register_base
                      + (sizeof_idu_per_processor_register_block
                      + (sizeof_idu_per_processor_register_block
                         * max_nr_interrupt_destinations));
                         * max_nr_interrupt_destinations));
    if (opic->idu.size != correct_size
    if (opic->idu.size != correct_size
        && opic->idu.size != alternate_size)
        && opic->idu.size != alternate_size)
      device_error(me, "interrupt delivery unit has incorrect size, should be 0x%x or 0x%x",
      device_error(me, "interrupt delivery unit has incorrect size, should be 0x%x or 0x%x",
                   correct_size, alternate_size);
                   correct_size, alternate_size);
    DTRACE(opic, ("interrupt delivery unit - address %d:0x%lx, size 0x%lx\n",
    DTRACE(opic, ("interrupt delivery unit - address %d:0x%lx, size 0x%lx\n",
                  (int)opic->idu.space,
                  (int)opic->idu.space,
                  (unsigned long)opic->idu.address,
                  (unsigned long)opic->idu.address,
                  (unsigned long)opic->idu.size));
                  (unsigned long)opic->idu.size));
  }
  }
 
 
  /* initialize the init interrupts */
  /* initialize the init interrupts */
  opic->init = 0;
  opic->init = 0;
 
 
 
 
  /* vendor ident */
  /* vendor ident */
  if (device_find_property(me, "vendor-identification") != NULL)
  if (device_find_property(me, "vendor-identification") != NULL)
    opic->vendor_identification = device_find_integer_property(me, "vendor-identification");
    opic->vendor_identification = device_find_integer_property(me, "vendor-identification");
  else
  else
    opic->vendor_identification = 0;
    opic->vendor_identification = 0;
 
 
  /* misc registers */
  /* misc registers */
  opic->spurious_vector = 0xff;
  opic->spurious_vector = 0xff;
 
 
}
}
 
 
 
 
/* interrupt related actions */
/* interrupt related actions */
 
 
static void
static void
assert_interrupt(device *me,
assert_interrupt(device *me,
                 hw_opic_device *opic,
                 hw_opic_device *opic,
                 opic_interrupt_destination *dest)
                 opic_interrupt_destination *dest)
{
{
  ASSERT(dest >= opic->interrupt_destination);
  ASSERT(dest >= opic->interrupt_destination);
  ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations);
  ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations);
  DTRACE(opic, ("assert interrupt - intr port %d\n", dest->intr_port));
  DTRACE(opic, ("assert interrupt - intr port %d\n", dest->intr_port));
  device_interrupt_event(me, dest->intr_port, 1, NULL, 0);
  device_interrupt_event(me, dest->intr_port, 1, NULL, 0);
}
}
 
 
 
 
static void
static void
negate_interrupt(device *me,
negate_interrupt(device *me,
                 hw_opic_device *opic,
                 hw_opic_device *opic,
                 opic_interrupt_destination *dest)
                 opic_interrupt_destination *dest)
{
{
  ASSERT(dest >= opic->interrupt_destination);
  ASSERT(dest >= opic->interrupt_destination);
  ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations);
  ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations);
  DTRACE(opic, ("negate interrupt - intr port %d\n", dest->intr_port));
  DTRACE(opic, ("negate interrupt - intr port %d\n", dest->intr_port));
  device_interrupt_event(me, dest->intr_port, 0, NULL, 0);
  device_interrupt_event(me, dest->intr_port, 0, NULL, 0);
}
}
 
 
 
 
static int
static int
can_deliver(device *me,
can_deliver(device *me,
            opic_interrupt_source *source,
            opic_interrupt_source *source,
            opic_interrupt_destination *dest)
            opic_interrupt_destination *dest)
{
{
  return (source != NULL && dest != NULL
  return (source != NULL && dest != NULL
          && source->priority > dest->base_priority
          && source->priority > dest->base_priority
          && (dest->current_in_service == NULL
          && (dest->current_in_service == NULL
              || source->priority > dest->current_in_service->priority));
              || source->priority > dest->current_in_service->priority));
}
}
 
 
 
 
static unsigned
static unsigned
deliver_pending(device *me,
deliver_pending(device *me,
                hw_opic_device *opic,
                hw_opic_device *opic,
                opic_interrupt_destination *dest)
                opic_interrupt_destination *dest)
{
{
  ASSERT(can_deliver(me, dest->current_pending, dest));
  ASSERT(can_deliver(me, dest->current_pending, dest));
  dest->current_in_service = dest->current_pending;
  dest->current_in_service = dest->current_pending;
  dest->current_in_service->in_service |= dest->bit;
  dest->current_in_service->in_service |= dest->bit;
  if (!dest->current_pending->is_level_triggered) {
  if (!dest->current_pending->is_level_triggered) {
    if (dest->current_pending->is_multicast)
    if (dest->current_pending->is_multicast)
      dest->current_pending->pending &= ~dest->bit;
      dest->current_pending->pending &= ~dest->bit;
    else
    else
      dest->current_pending->pending = 0;
      dest->current_pending->pending = 0;
  }
  }
  dest->current_pending = NULL;
  dest->current_pending = NULL;
  negate_interrupt(me, opic, dest);
  negate_interrupt(me, opic, dest);
  return dest->current_in_service->vector;
  return dest->current_in_service->vector;
}
}
 
 
 
 
typedef enum {
typedef enum {
  pending_interrupt,
  pending_interrupt,
  in_service_interrupt,
  in_service_interrupt,
} interrupt_class;
} interrupt_class;
 
 
static opic_interrupt_source *
static opic_interrupt_source *
find_interrupt_for_dest(device *me,
find_interrupt_for_dest(device *me,
                        hw_opic_device *opic,
                        hw_opic_device *opic,
                        opic_interrupt_destination *dest,
                        opic_interrupt_destination *dest,
                        interrupt_class class)
                        interrupt_class class)
{
{
  int i;
  int i;
  opic_interrupt_source *pending = NULL;
  opic_interrupt_source *pending = NULL;
  for (i = 0; i < opic->nr_interrupt_sources; i++) {
  for (i = 0; i < opic->nr_interrupt_sources; i++) {
    opic_interrupt_source *src = &opic->interrupt_source[i];
    opic_interrupt_source *src = &opic->interrupt_source[i];
    /* is this a potential hit? */
    /* is this a potential hit? */
    switch (class) {
    switch (class) {
    case in_service_interrupt:
    case in_service_interrupt:
      if ((src->in_service & dest->bit) == 0)
      if ((src->in_service & dest->bit) == 0)
        continue;
        continue;
      break;
      break;
    case pending_interrupt:
    case pending_interrupt:
      if ((src->pending & dest->bit) == 0)
      if ((src->pending & dest->bit) == 0)
        continue;
        continue;
      break;
      break;
    }
    }
    /* see if it is the highest priority */
    /* see if it is the highest priority */
    if (pending == NULL)
    if (pending == NULL)
      pending = src;
      pending = src;
    else if (src->priority > pending->priority)
    else if (src->priority > pending->priority)
      pending = src;
      pending = src;
  }
  }
  return pending;
  return pending;
}
}
 
 
 
 
static opic_interrupt_destination *
static opic_interrupt_destination *
find_lowest_dest(device *me,
find_lowest_dest(device *me,
                 hw_opic_device *opic,
                 hw_opic_device *opic,
                 opic_interrupt_source *src)
                 opic_interrupt_source *src)
{
{
  int i;
  int i;
  opic_interrupt_destination *lowest = NULL;
  opic_interrupt_destination *lowest = NULL;
  for (i = 0; i < opic->nr_interrupt_destinations; i++) {
  for (i = 0; i < opic->nr_interrupt_destinations; i++) {
    opic_interrupt_destination *dest = &opic->interrupt_destination[i];
    opic_interrupt_destination *dest = &opic->interrupt_destination[i];
    if (src->destination & dest->bit) {
    if (src->destination & dest->bit) {
      if (dest->base_priority < src->priority) {
      if (dest->base_priority < src->priority) {
        if (lowest == NULL)
        if (lowest == NULL)
          lowest = dest;
          lowest = dest;
        else if (lowest->base_priority > dest->base_priority)
        else if (lowest->base_priority > dest->base_priority)
          lowest = dest;
          lowest = dest;
        else if (lowest->current_in_service != NULL
        else if (lowest->current_in_service != NULL
                 && dest->current_in_service == NULL)
                 && dest->current_in_service == NULL)
          lowest = dest; /* not doing anything */
          lowest = dest; /* not doing anything */
        else if (lowest->current_in_service != NULL
        else if (lowest->current_in_service != NULL
                 && dest->current_in_service != NULL
                 && dest->current_in_service != NULL
                 && (lowest->current_in_service->priority
                 && (lowest->current_in_service->priority
                     > dest->current_in_service->priority))
                     > dest->current_in_service->priority))
          lowest = dest; /* less urgent */
          lowest = dest; /* less urgent */
        /* FIXME - need to be more fair */
        /* FIXME - need to be more fair */
      }
      }
    }
    }
  }
  }
  return lowest;
  return lowest;
}
}
 
 
 
 
static void
static void
handle_interrupt(device *me,
handle_interrupt(device *me,
                 hw_opic_device *opic,
                 hw_opic_device *opic,
                 opic_interrupt_source *src,
                 opic_interrupt_source *src,
                 int asserted)
                 int asserted)
{
{
  if (src->is_masked) {
  if (src->is_masked) {
    DTRACE(opic, ("interrupt %d - ignore masked\n", src->nr));
    DTRACE(opic, ("interrupt %d - ignore masked\n", src->nr));
  }
  }
  else if (src->is_multicast) {
  else if (src->is_multicast) {
    /* always try to deliver multicast interrupts - just easier */
    /* always try to deliver multicast interrupts - just easier */
    int i;
    int i;
    ASSERT(!src->is_level_triggered);
    ASSERT(!src->is_level_triggered);
    ASSERT(src->is_positive_polarity);
    ASSERT(src->is_positive_polarity);
    ASSERT(asserted);
    ASSERT(asserted);
    for (i = 0; i < opic->nr_interrupt_destinations; i++) {
    for (i = 0; i < opic->nr_interrupt_destinations; i++) {
      opic_interrupt_destination *dest = &opic->interrupt_destination[i];
      opic_interrupt_destination *dest = &opic->interrupt_destination[i];
      if (src->destination & dest->bit) {
      if (src->destination & dest->bit) {
        if (src->pending & dest->bit) {
        if (src->pending & dest->bit) {
          DTRACE(opic, ("interrupt %d - multicast still pending to %d\n",
          DTRACE(opic, ("interrupt %d - multicast still pending to %d\n",
                        src->nr, dest->nr));
                        src->nr, dest->nr));
        }
        }
        else if (can_deliver(me, src, dest)) {
        else if (can_deliver(me, src, dest)) {
          dest->current_pending = src;
          dest->current_pending = src;
          src->pending |= dest->bit;
          src->pending |= dest->bit;
          assert_interrupt(me, opic, dest);
          assert_interrupt(me, opic, dest);
          DTRACE(opic, ("interrupt %d - multicast to %d\n",
          DTRACE(opic, ("interrupt %d - multicast to %d\n",
                        src->nr, dest->nr));
                        src->nr, dest->nr));
        }
        }
        else {
        else {
          src->pending |= dest->bit;
          src->pending |= dest->bit;
          DTRACE(opic, ("interrupt %d - multicast pending to %d\n",
          DTRACE(opic, ("interrupt %d - multicast pending to %d\n",
                        src->nr, dest->nr));
                        src->nr, dest->nr));
        }
        }
      }
      }
    }
    }
  }
  }
  else if (src->is_level_triggered
  else if (src->is_level_triggered
           && src->is_positive_polarity
           && src->is_positive_polarity
           && !asserted) {
           && !asserted) {
    if (src->pending)
    if (src->pending)
      DTRACE(opic, ("interrupt %d - ignore withdrawn (active high)\n",
      DTRACE(opic, ("interrupt %d - ignore withdrawn (active high)\n",
                    src->nr));
                    src->nr));
    else
    else
      DTRACE(opic, ("interrupt %d - ignore low level (active high)\n",
      DTRACE(opic, ("interrupt %d - ignore low level (active high)\n",
                    src->nr));
                    src->nr));
    ASSERT(!src->is_multicast);
    ASSERT(!src->is_multicast);
    src->pending = 0;
    src->pending = 0;
  }
  }
  else if (src->is_level_triggered
  else if (src->is_level_triggered
           && !src->is_positive_polarity
           && !src->is_positive_polarity
           && asserted) {
           && asserted) {
    if (src->pending)
    if (src->pending)
      DTRACE(opic, ("interrupt %d - ignore withdrawn (active low)\n",
      DTRACE(opic, ("interrupt %d - ignore withdrawn (active low)\n",
                    src->nr));
                    src->nr));
    else
    else
      DTRACE(opic, ("interrupt %d - ignore high level (active low)\n",
      DTRACE(opic, ("interrupt %d - ignore high level (active low)\n",
                    src->nr));
                    src->nr));
 
 
    ASSERT(!src->is_multicast);
    ASSERT(!src->is_multicast);
    src->pending = 0;
    src->pending = 0;
  }
  }
  else if (!src->is_level_triggered
  else if (!src->is_level_triggered
           && src->is_positive_polarity
           && src->is_positive_polarity
           && !asserted) {
           && !asserted) {
    DTRACE(opic, ("interrupt %d - ignore falling edge (positive edge trigered)\n",
    DTRACE(opic, ("interrupt %d - ignore falling edge (positive edge trigered)\n",
                  src->nr));
                  src->nr));
  }
  }
  else if (!src->is_level_triggered
  else if (!src->is_level_triggered
           && !src->is_positive_polarity
           && !src->is_positive_polarity
           && asserted) {
           && asserted) {
    DTRACE(opic, ("interrupt %d - ignore rising edge (negative edge trigered)\n",
    DTRACE(opic, ("interrupt %d - ignore rising edge (negative edge trigered)\n",
                  src->nr));
                  src->nr));
  }
  }
  else if (src->in_service != 0) {
  else if (src->in_service != 0) {
    /* leave the interrupt where it is */
    /* leave the interrupt where it is */
    ASSERT(!src->is_multicast);
    ASSERT(!src->is_multicast);
    ASSERT(src->pending == 0 || src->pending == src->in_service);
    ASSERT(src->pending == 0 || src->pending == src->in_service);
    src->pending = src->in_service;
    src->pending = src->in_service;
    DTRACE(opic, ("interrupt %ld - ignore already in service to 0x%lx\n",
    DTRACE(opic, ("interrupt %ld - ignore already in service to 0x%lx\n",
                  (long)src->nr, (long)src->in_service));
                  (long)src->nr, (long)src->in_service));
  }
  }
  else if (src->pending != 0) {
  else if (src->pending != 0) {
    DTRACE(opic, ("interrupt %ld - ignore still pending to 0x%lx\n",
    DTRACE(opic, ("interrupt %ld - ignore still pending to 0x%lx\n",
                  (long)src->nr, (long)src->pending));
                  (long)src->nr, (long)src->pending));
  }
  }
  else {
  else {
    /* delivery is needed */
    /* delivery is needed */
    opic_interrupt_destination *dest = find_lowest_dest(me, opic, src);
    opic_interrupt_destination *dest = find_lowest_dest(me, opic, src);
    if (can_deliver(me, src, dest)) {
    if (can_deliver(me, src, dest)) {
      dest->current_pending = src;
      dest->current_pending = src;
      src->pending = dest->bit;
      src->pending = dest->bit;
      DTRACE(opic, ("interrupt %d - delivered to %d\n", src->nr, dest->nr));
      DTRACE(opic, ("interrupt %d - delivered to %d\n", src->nr, dest->nr));
      assert_interrupt(me, opic, dest);
      assert_interrupt(me, opic, dest);
    }
    }
    else {
    else {
      src->pending = src->destination; /* any can take this */
      src->pending = src->destination; /* any can take this */
      DTRACE(opic, ("interrupt %ld - pending to 0x%lx\n",
      DTRACE(opic, ("interrupt %ld - pending to 0x%lx\n",
                    (long)src->nr, (long)src->pending));
                    (long)src->nr, (long)src->pending));
    }
    }
  }
  }
}
}
 
 
static unsigned
static unsigned
do_interrupt_acknowledge_register_N_read(device *me,
do_interrupt_acknowledge_register_N_read(device *me,
                                         hw_opic_device *opic,
                                         hw_opic_device *opic,
                                         int dest_nr)
                                         int dest_nr)
{
{
  opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr];
  opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr];
  unsigned vector;
  unsigned vector;
 
 
  ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations);
  ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations);
  ASSERT(dest_nr == dest->nr);
  ASSERT(dest_nr == dest->nr);
 
 
  /* try the current pending */
  /* try the current pending */
  if (can_deliver(me, dest->current_pending, dest)) {
  if (can_deliver(me, dest->current_pending, dest)) {
    ASSERT(dest->current_pending->pending & dest->bit);
    ASSERT(dest->current_pending->pending & dest->bit);
    vector = deliver_pending(me, opic, dest);
    vector = deliver_pending(me, opic, dest);
    DTRACE(opic, ("interrupt ack %d - entering %d (pending) - vector %d (%d), priority %d\n",
    DTRACE(opic, ("interrupt ack %d - entering %d (pending) - vector %d (%d), priority %d\n",
                  dest->nr,
                  dest->nr,
                  dest->current_in_service->nr,
                  dest->current_in_service->nr,
                  dest->current_in_service->vector, vector,
                  dest->current_in_service->vector, vector,
                  dest->current_in_service->priority));
                  dest->current_in_service->priority));
  }
  }
  else {
  else {
    /* try for something else */
    /* try for something else */
    dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt);
    dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt);
    if (can_deliver(me, dest->current_pending, dest)) {
    if (can_deliver(me, dest->current_pending, dest)) {
      vector = deliver_pending(me, opic, dest);
      vector = deliver_pending(me, opic, dest);
      DTRACE(opic, ("interrupt ack %d - entering %d (not pending) - vector %d (%d), priority %d\n",
      DTRACE(opic, ("interrupt ack %d - entering %d (not pending) - vector %d (%d), priority %d\n",
                    dest->nr,
                    dest->nr,
                    dest->current_in_service->nr,
                    dest->current_in_service->nr,
                    dest->current_in_service->vector, vector,
                    dest->current_in_service->vector, vector,
                    dest->current_in_service->priority));
                    dest->current_in_service->priority));
    }
    }
    else {
    else {
      dest->current_pending = NULL;
      dest->current_pending = NULL;
      vector = opic->spurious_vector;
      vector = opic->spurious_vector;
      DTRACE(opic, ("interrupt ack %d - spurious interrupt %d\n",
      DTRACE(opic, ("interrupt ack %d - spurious interrupt %d\n",
                    dest->nr, vector));
                    dest->nr, vector));
    }
    }
  }
  }
  return vector;
  return vector;
}
}
 
 
 
 
static void
static void
do_end_of_interrupt_register_N_write(device *me,
do_end_of_interrupt_register_N_write(device *me,
                                     hw_opic_device *opic,
                                     hw_opic_device *opic,
                                     int dest_nr,
                                     int dest_nr,
                                     unsigned reg)
                                     unsigned reg)
{
{
  opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr];
  opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr];
 
 
  ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations);
  ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations);
  ASSERT(dest_nr == dest->nr);
  ASSERT(dest_nr == dest->nr);
 
 
  /* check the value written is zero */
  /* check the value written is zero */
  if (reg != 0) {
  if (reg != 0) {
    DTRACE(opic, ("eoi %d - ignoring nonzero value\n", dest->nr));
    DTRACE(opic, ("eoi %d - ignoring nonzero value\n", dest->nr));
  }
  }
 
 
  /* user doing wierd things? */
  /* user doing wierd things? */
  if (dest->current_in_service == NULL) {
  if (dest->current_in_service == NULL) {
    DTRACE(opic, ("eoi %d - strange, no current interrupt\n", dest->nr));
    DTRACE(opic, ("eoi %d - strange, no current interrupt\n", dest->nr));
    return;
    return;
  }
  }
 
 
  /* an internal stuff up? */
  /* an internal stuff up? */
  if (!(dest->current_in_service->in_service & dest->bit)) {
  if (!(dest->current_in_service->in_service & dest->bit)) {
    device_error(me, "eoi %d - current interrupt not in service", dest->nr);
    device_error(me, "eoi %d - current interrupt not in service", dest->nr);
  }
  }
 
 
  /* find what was probably the previous in service interrupt */
  /* find what was probably the previous in service interrupt */
  dest->current_in_service->in_service &= ~dest->bit;
  dest->current_in_service->in_service &= ~dest->bit;
  DTRACE(opic, ("eoi %d - ending %d - priority %d, vector %d\n",
  DTRACE(opic, ("eoi %d - ending %d - priority %d, vector %d\n",
                dest->nr,
                dest->nr,
                dest->current_in_service->nr,
                dest->current_in_service->nr,
                dest->current_in_service->priority,
                dest->current_in_service->priority,
                dest->current_in_service->vector));
                dest->current_in_service->vector));
  dest->current_in_service = find_interrupt_for_dest(me, opic, dest, in_service_interrupt);
  dest->current_in_service = find_interrupt_for_dest(me, opic, dest, in_service_interrupt);
  if (dest->current_in_service != NULL)
  if (dest->current_in_service != NULL)
    DTRACE(opic, ("eoi %d - resuming %d - priority %d, vector %d\n",
    DTRACE(opic, ("eoi %d - resuming %d - priority %d, vector %d\n",
                  dest->nr,
                  dest->nr,
                  dest->current_in_service->nr,
                  dest->current_in_service->nr,
                  dest->current_in_service->priority,
                  dest->current_in_service->priority,
                  dest->current_in_service->vector));
                  dest->current_in_service->vector));
  else
  else
    DTRACE(opic, ("eoi %d - resuming none\n", dest->nr));
    DTRACE(opic, ("eoi %d - resuming none\n", dest->nr));
 
 
  /* check to see if that shouldn't be interrupted */
  /* check to see if that shouldn't be interrupted */
  dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt);
  dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt);
  if (can_deliver(me, dest->current_pending, dest)) {
  if (can_deliver(me, dest->current_pending, dest)) {
    ASSERT(dest->current_pending->pending & dest->bit);
    ASSERT(dest->current_pending->pending & dest->bit);
    assert_interrupt(me, opic, dest);
    assert_interrupt(me, opic, dest);
  }
  }
  else {
  else {
    dest->current_pending = NULL;
    dest->current_pending = NULL;
  }
  }
}
}
 
 
 
 
static void
static void
decode_opic_address(device *me,
decode_opic_address(device *me,
                    hw_opic_device *opic,
                    hw_opic_device *opic,
                    int space,
                    int space,
                    unsigned_word address,
                    unsigned_word address,
                    unsigned nr_bytes,
                    unsigned nr_bytes,
                    opic_register *type,
                    opic_register *type,
                    int *index)
                    int *index)
{
{
  int isb = 0;
  int isb = 0;
 
 
  /* is the size valid? */
  /* is the size valid? */
  if (nr_bytes != 4) {
  if (nr_bytes != 4) {
    *type = invalid_opic_register;
    *type = invalid_opic_register;
    *index = -1;
    *index = -1;
    return;
    return;
  }
  }
 
 
  /* try for a per-processor register within the interrupt delivery
  /* try for a per-processor register within the interrupt delivery
     unit */
     unit */
  if (space == opic->idu.space
  if (space == opic->idu.space
      && address >= (opic->idu.address + idu_per_processor_register_base)
      && address >= (opic->idu.address + idu_per_processor_register_base)
      && address < (opic->idu.address + idu_per_processor_register_base
      && address < (opic->idu.address + idu_per_processor_register_base
                    + (sizeof_idu_per_processor_register_block
                    + (sizeof_idu_per_processor_register_block
                       * opic->nr_interrupt_destinations))) {
                       * opic->nr_interrupt_destinations))) {
    unsigned_word block_offset = (address
    unsigned_word block_offset = (address
                                  - opic->idu.address
                                  - opic->idu.address
                                  - idu_per_processor_register_base);
                                  - idu_per_processor_register_base);
    unsigned_word offset = block_offset % sizeof_idu_per_processor_register_block;
    unsigned_word offset = block_offset % sizeof_idu_per_processor_register_block;
    *index = block_offset / sizeof_idu_per_processor_register_block;
    *index = block_offset / sizeof_idu_per_processor_register_block;
    switch (offset) {
    switch (offset) {
    case 0x040:
    case 0x040:
      *type = ipi_N_dispatch_register;
      *type = ipi_N_dispatch_register;
      *index = 0;
      *index = 0;
      break;
      break;
    case 0x050:
    case 0x050:
      *type = ipi_N_dispatch_register;
      *type = ipi_N_dispatch_register;
      *index = 1;
      *index = 1;
      break;
      break;
    case 0x060:
    case 0x060:
      *type = ipi_N_dispatch_register;
      *type = ipi_N_dispatch_register;
      *index = 2;
      *index = 2;
      break;
      break;
    case 0x070:
    case 0x070:
      *type = ipi_N_dispatch_register;
      *type = ipi_N_dispatch_register;
      *index = 3;
      *index = 3;
      break;
      break;
    case 0x080:
    case 0x080:
      *type = current_task_priority_register_N;
      *type = current_task_priority_register_N;
      break;
      break;
    case 0x0a0:
    case 0x0a0:
      *type = interrupt_acknowledge_register_N;
      *type = interrupt_acknowledge_register_N;
      break;
      break;
    case 0x0b0:
    case 0x0b0:
      *type = end_of_interrupt_register_N;
      *type = end_of_interrupt_register_N;
      break;
      break;
    default:
    default:
      *type = invalid_opic_register;
      *type = invalid_opic_register;
      break;
      break;
    }
    }
    DTRACE(opic, ("per-processor register %d:0x%lx - %s[%d]\n",
    DTRACE(opic, ("per-processor register %d:0x%lx - %s[%d]\n",
                  space, (unsigned long)address,
                  space, (unsigned long)address,
                  opic_register_name(*type),
                  opic_register_name(*type),
                  *index));
                  *index));
    return;
    return;
  }
  }
 
 
  /* try for an interrupt source unit */
  /* try for an interrupt source unit */
  for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
  for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
    if (opic->isu_block[isb].space == space
    if (opic->isu_block[isb].space == space
        && address >= opic->isu_block[isb].address
        && address >= opic->isu_block[isb].address
        && address < (opic->isu_block[isb].address + opic->isu_block[isb].size)) {
        && address < (opic->isu_block[isb].address + opic->isu_block[isb].size)) {
      unsigned_word block_offset = address - opic->isu_block[isb].address;
      unsigned_word block_offset = address - opic->isu_block[isb].address;
      unsigned_word offset = block_offset % sizeof_isu_register_block;
      unsigned_word offset = block_offset % sizeof_isu_register_block;
      *index = (opic->isu_block[isb].int_number
      *index = (opic->isu_block[isb].int_number
                + (block_offset / sizeof_isu_register_block));
                + (block_offset / sizeof_isu_register_block));
      switch (offset) {
      switch (offset) {
      case 0x00:
      case 0x00:
        *type = interrupt_source_N_vector_priority_register;
        *type = interrupt_source_N_vector_priority_register;
        break;
        break;
      case 0x10:
      case 0x10:
        *type = interrupt_source_N_destination_register;
        *type = interrupt_source_N_destination_register;
        break;
        break;
      default:
      default:
        *type = invalid_opic_register;
        *type = invalid_opic_register;
        break;
        break;
      }
      }
      DTRACE(opic, ("isu register %d:0x%lx - %s[%d]\n",
      DTRACE(opic, ("isu register %d:0x%lx - %s[%d]\n",
                    space, (unsigned long)address,
                    space, (unsigned long)address,
                    opic_register_name(*type),
                    opic_register_name(*type),
                    *index));
                    *index));
      return;
      return;
    }
    }
  }
  }
 
 
  /* try for a timer */
  /* try for a timer */
  if (space == opic->idu.space
  if (space == opic->idu.space
      && address >= (opic->idu.address + idu_timer_base)
      && address >= (opic->idu.address + idu_timer_base)
      && address < (opic->idu.address + idu_timer_base
      && address < (opic->idu.address + idu_timer_base
                    + opic->nr_timer_interrupts * sizeof_timer_register_block)) {
                    + opic->nr_timer_interrupts * sizeof_timer_register_block)) {
    unsigned_word offset = address % sizeof_timer_register_block;
    unsigned_word offset = address % sizeof_timer_register_block;
    *index = ((address - opic->idu.address - idu_timer_base)
    *index = ((address - opic->idu.address - idu_timer_base)
              / sizeof_timer_register_block);
              / sizeof_timer_register_block);
    switch (offset) {
    switch (offset) {
    case 0x00:
    case 0x00:
      *type = timer_N_current_count_register;
      *type = timer_N_current_count_register;
      break;
      break;
    case 0x10:
    case 0x10:
      *type = timer_N_base_count_register;
      *type = timer_N_base_count_register;
      break;
      break;
    case 0x20:
    case 0x20:
      *type = timer_N_vector_priority_register;
      *type = timer_N_vector_priority_register;
      break;
      break;
    case 0x30:
    case 0x30:
      *type = timer_N_destination_register;
      *type = timer_N_destination_register;
      break;
      break;
    default:
    default:
      *type = invalid_opic_register;
      *type = invalid_opic_register;
      break;
      break;
    }
    }
    DTRACE(opic, ("timer register %d:0x%lx - %s[%d]\n",
    DTRACE(opic, ("timer register %d:0x%lx - %s[%d]\n",
                  space, (unsigned long)address,
                  space, (unsigned long)address,
                  opic_register_name(*type),
                  opic_register_name(*type),
                  *index));
                  *index));
    return;
    return;
  }
  }
 
 
  /* finally some other misc global register */
  /* finally some other misc global register */
  if (space == opic->idu.space
  if (space == opic->idu.space
      && address >= opic->idu.address
      && address >= opic->idu.address
      && address < opic->idu.address + opic->idu.size) {
      && address < opic->idu.address + opic->idu.size) {
    unsigned_word block_offset = address - opic->idu.address;
    unsigned_word block_offset = address - opic->idu.address;
    switch (block_offset) {
    switch (block_offset) {
    case 0x010f0:
    case 0x010f0:
      *type = timer_frequency_reporting_register;
      *type = timer_frequency_reporting_register;
      *index = -1;
      *index = -1;
      break;
      break;
    case 0x010e0:
    case 0x010e0:
      *type = spurious_vector_register;
      *type = spurious_vector_register;
      *index = -1;
      *index = -1;
      break;
      break;
    case 0x010d0:
    case 0x010d0:
    case 0x010c0:
    case 0x010c0:
    case 0x010b0:
    case 0x010b0:
    case 0x010a0:
    case 0x010a0:
      *type = ipi_N_vector_priority_register;
      *type = ipi_N_vector_priority_register;
      *index = (block_offset - 0x010a0) / 16;
      *index = (block_offset - 0x010a0) / 16;
      break;
      break;
    case 0x01090:
    case 0x01090:
      *type = processor_init_register;
      *type = processor_init_register;
      *index = -1;
      *index = -1;
      break;
      break;
    case 0x01080:
    case 0x01080:
      *type = vendor_identification_register;
      *type = vendor_identification_register;
      *index = -1;
      *index = -1;
      break;
      break;
    case 0x01020:
    case 0x01020:
      *type = global_configuration_register_N;
      *type = global_configuration_register_N;
      *index = 0;
      *index = 0;
      break;
      break;
    case 0x01000:
    case 0x01000:
      *type = feature_reporting_register_N;
      *type = feature_reporting_register_N;
      *index = 0;
      *index = 0;
      break;
      break;
    default:
    default:
      *type = invalid_opic_register;
      *type = invalid_opic_register;
      *index = -1;
      *index = -1;
      break;
      break;
    }
    }
    DTRACE(opic, ("global register %d:0x%lx - %s[%d]\n",
    DTRACE(opic, ("global register %d:0x%lx - %s[%d]\n",
                  space, (unsigned long)address,
                  space, (unsigned long)address,
                  opic_register_name(*type),
                  opic_register_name(*type),
                  *index));
                  *index));
    return;
    return;
  }
  }
 
 
  /* nothing matched */
  /* nothing matched */
  *type = invalid_opic_register;
  *type = invalid_opic_register;
  DTRACE(opic, ("invalid register %d:0x%lx\n",
  DTRACE(opic, ("invalid register %d:0x%lx\n",
                space, (unsigned long)address));
                space, (unsigned long)address));
  return;
  return;
}
}
 
 
 
 
/* Processor init register:
/* Processor init register:
 
 
   The bits in this register (one per processor) are directly wired to
   The bits in this register (one per processor) are directly wired to
   output "init" interrupt ports. */
   output "init" interrupt ports. */
 
 
static unsigned
static unsigned
do_processor_init_register_read(device *me,
do_processor_init_register_read(device *me,
                                hw_opic_device *opic)
                                hw_opic_device *opic)
{
{
  unsigned reg = opic->init;
  unsigned reg = opic->init;
  DTRACE(opic, ("processor init register - read 0x%lx\n",
  DTRACE(opic, ("processor init register - read 0x%lx\n",
                (long)reg));
                (long)reg));
  return reg;
  return reg;
}
}
 
 
static void
static void
do_processor_init_register_write(device *me,
do_processor_init_register_write(device *me,
                                 hw_opic_device *opic,
                                 hw_opic_device *opic,
                                 unsigned reg)
                                 unsigned reg)
{
{
  int i;
  int i;
  for (i = 0; i < opic->nr_interrupt_destinations; i++) {
  for (i = 0; i < opic->nr_interrupt_destinations; i++) {
    opic_interrupt_destination *dest = &opic->interrupt_destination[i];
    opic_interrupt_destination *dest = &opic->interrupt_destination[i];
    if ((reg & dest->bit) != (opic->init & dest->bit)) {
    if ((reg & dest->bit) != (opic->init & dest->bit)) {
      if (reg & dest->bit) {
      if (reg & dest->bit) {
        DTRACE(opic, ("processor init register - write 0x%lx - asserting init%d\n",
        DTRACE(opic, ("processor init register - write 0x%lx - asserting init%d\n",
                      (long)reg, i));
                      (long)reg, i));
        opic->init |= dest->bit;
        opic->init |= dest->bit;
        device_interrupt_event(me, dest->init_port, 1, NULL, 0);
        device_interrupt_event(me, dest->init_port, 1, NULL, 0);
      }
      }
      else {
      else {
        DTRACE(opic, ("processor init register - write 0x%lx - negating init%d\n",
        DTRACE(opic, ("processor init register - write 0x%lx - negating init%d\n",
                      (long)reg, i));
                      (long)reg, i));
        opic->init &= ~dest->bit;
        opic->init &= ~dest->bit;
        device_interrupt_event(me, dest->init_port, 0, NULL, 0);
        device_interrupt_event(me, dest->init_port, 0, NULL, 0);
      }
      }
    }
    }
  }
  }
}
}
 
 
 
 
 
 
/* Interrupt Source Vector/Priority Register: */
/* Interrupt Source Vector/Priority Register: */
 
 
static unsigned
static unsigned
read_vector_priority_register(device *me,
read_vector_priority_register(device *me,
                              hw_opic_device *opic,
                              hw_opic_device *opic,
                              opic_interrupt_source *interrupt,
                              opic_interrupt_source *interrupt,
                              const char *reg_name,
                              const char *reg_name,
                              int reg_index)
                              int reg_index)
{
{
  unsigned reg;
  unsigned reg;
  reg = 0;
  reg = 0;
  reg |= interrupt->is_masked;
  reg |= interrupt->is_masked;
  reg |= (interrupt->in_service || interrupt->pending
  reg |= (interrupt->in_service || interrupt->pending
          ? isu_active_bit : 0); /* active */
          ? isu_active_bit : 0); /* active */
  reg |= interrupt->is_multicast;
  reg |= interrupt->is_multicast;
  reg |= interrupt->is_positive_polarity;
  reg |= interrupt->is_positive_polarity;
  reg |= interrupt->is_level_triggered; /* sense? */
  reg |= interrupt->is_level_triggered; /* sense? */
  reg |= interrupt->priority << isu_priority_shift;
  reg |= interrupt->priority << isu_priority_shift;
  reg |= interrupt->vector;
  reg |= interrupt->vector;
  DTRACE(opic, ("%s %d vector/priority register - read 0x%lx\n",
  DTRACE(opic, ("%s %d vector/priority register - read 0x%lx\n",
                reg_name, reg_index, (unsigned long)reg));
                reg_name, reg_index, (unsigned long)reg));
  return reg;
  return reg;
}
}
 
 
static unsigned
static unsigned
do_interrupt_source_N_vector_priority_register_read(device *me,
do_interrupt_source_N_vector_priority_register_read(device *me,
                                                    hw_opic_device *opic,
                                                    hw_opic_device *opic,
                                                    int index)
                                                    int index)
{
{
  unsigned reg;
  unsigned reg;
  ASSERT(index < opic->nr_external_interrupts);
  ASSERT(index < opic->nr_external_interrupts);
  reg = read_vector_priority_register(me, opic,
  reg = read_vector_priority_register(me, opic,
                                      &opic->interrupt_source[index],
                                      &opic->interrupt_source[index],
                                      "interrupt source", index);
                                      "interrupt source", index);
  return reg;
  return reg;
}
}
 
 
static void
static void
write_vector_priority_register(device *me,
write_vector_priority_register(device *me,
                               hw_opic_device *opic,
                               hw_opic_device *opic,
                               opic_interrupt_source *interrupt,
                               opic_interrupt_source *interrupt,
                               unsigned reg,
                               unsigned reg,
                               const char *reg_name,
                               const char *reg_name,
                               int reg_index)
                               int reg_index)
{
{
  interrupt->is_masked = (reg & isu_mask_bit);
  interrupt->is_masked = (reg & isu_mask_bit);
  interrupt->is_multicast = (reg & isu_multicast_bit);
  interrupt->is_multicast = (reg & isu_multicast_bit);
  interrupt->is_positive_polarity = (reg & isu_positive_polarity_bit);
  interrupt->is_positive_polarity = (reg & isu_positive_polarity_bit);
  interrupt->is_level_triggered = (reg & isu_level_triggered_bit);
  interrupt->is_level_triggered = (reg & isu_level_triggered_bit);
  interrupt->priority = ((reg >> isu_priority_shift)
  interrupt->priority = ((reg >> isu_priority_shift)
                         % max_nr_task_priorities);
                         % max_nr_task_priorities);
  interrupt->vector = (reg & isu_vector_bits);
  interrupt->vector = (reg & isu_vector_bits);
  DTRACE(opic, ("%s %d vector/priority register - write 0x%lx - %s%s%s-polarity, %s-triggered, priority %ld vector %ld\n",
  DTRACE(opic, ("%s %d vector/priority register - write 0x%lx - %s%s%s-polarity, %s-triggered, priority %ld vector %ld\n",
                reg_name,
                reg_name,
                reg_index,
                reg_index,
                (unsigned long)reg,
                (unsigned long)reg,
                interrupt->is_masked ? "masked, " : "",
                interrupt->is_masked ? "masked, " : "",
                interrupt->is_multicast ? "multicast, " : "",
                interrupt->is_multicast ? "multicast, " : "",
                interrupt->is_positive_polarity ? "positive" : "negative",
                interrupt->is_positive_polarity ? "positive" : "negative",
                interrupt->is_level_triggered ? "level" : "edge",
                interrupt->is_level_triggered ? "level" : "edge",
                (long)interrupt->priority,
                (long)interrupt->priority,
                (long)interrupt->vector));
                (long)interrupt->vector));
}
}
 
 
static void
static void
do_interrupt_source_N_vector_priority_register_write(device *me,
do_interrupt_source_N_vector_priority_register_write(device *me,
                                                     hw_opic_device *opic,
                                                     hw_opic_device *opic,
                                                     int index,
                                                     int index,
                                                     unsigned reg)
                                                     unsigned reg)
{
{
  ASSERT(index < opic->nr_external_interrupts);
  ASSERT(index < opic->nr_external_interrupts);
  reg &= ~isu_multicast_bit; /* disable multicast */
  reg &= ~isu_multicast_bit; /* disable multicast */
  write_vector_priority_register(me, opic,
  write_vector_priority_register(me, opic,
                                 &opic->interrupt_source[index],
                                 &opic->interrupt_source[index],
                                 reg, "interrupt source", index);
                                 reg, "interrupt source", index);
}
}
 
 
 
 
 
 
/* Interrupt Source Destination Register: */
/* Interrupt Source Destination Register: */
 
 
static unsigned
static unsigned
read_destination_register(device *me,
read_destination_register(device *me,
                          hw_opic_device *opic,
                          hw_opic_device *opic,
                          opic_interrupt_source *interrupt,
                          opic_interrupt_source *interrupt,
                          const char *reg_name,
                          const char *reg_name,
                          int reg_index)
                          int reg_index)
{
{
  unsigned long reg;
  unsigned long reg;
  reg = interrupt->destination;
  reg = interrupt->destination;
  DTRACE(opic, ("%s %d destination register - read 0x%lx\n",
  DTRACE(opic, ("%s %d destination register - read 0x%lx\n",
                reg_name, reg_index, reg));
                reg_name, reg_index, reg));
  return reg;
  return reg;
}
}
 
 
static unsigned
static unsigned
do_interrupt_source_N_destination_register_read(device *me,
do_interrupt_source_N_destination_register_read(device *me,
                                                hw_opic_device *opic,
                                                hw_opic_device *opic,
                                                int index)
                                                int index)
{
{
  unsigned reg;
  unsigned reg;
  ASSERT(index < opic->nr_external_interrupts);
  ASSERT(index < opic->nr_external_interrupts);
  reg = read_destination_register(me, opic, &opic->external_interrupt_source[index],
  reg = read_destination_register(me, opic, &opic->external_interrupt_source[index],
                                  "interrupt source", index);
                                  "interrupt source", index);
  return reg;
  return reg;
}
}
 
 
static void
static void
write_destination_register(device *me,
write_destination_register(device *me,
                           hw_opic_device *opic,
                           hw_opic_device *opic,
                           opic_interrupt_source *interrupt,
                           opic_interrupt_source *interrupt,
                           unsigned reg,
                           unsigned reg,
                           const char *reg_name,
                           const char *reg_name,
                           int reg_index)
                           int reg_index)
{
{
  reg &= (1 << opic->nr_interrupt_destinations) - 1; /* mask out invalid */
  reg &= (1 << opic->nr_interrupt_destinations) - 1; /* mask out invalid */
  DTRACE(opic, ("%s %d destination register - write 0x%x\n",
  DTRACE(opic, ("%s %d destination register - write 0x%x\n",
                reg_name, reg_index, reg));
                reg_name, reg_index, reg));
  interrupt->destination = reg;
  interrupt->destination = reg;
}
}
 
 
static void
static void
do_interrupt_source_N_destination_register_write(device *me,
do_interrupt_source_N_destination_register_write(device *me,
                                                 hw_opic_device *opic,
                                                 hw_opic_device *opic,
                                                 int index,
                                                 int index,
                                                 unsigned reg)
                                                 unsigned reg)
{
{
  ASSERT(index < opic->nr_external_interrupts);
  ASSERT(index < opic->nr_external_interrupts);
  write_destination_register(me, opic, &opic->external_interrupt_source[index],
  write_destination_register(me, opic, &opic->external_interrupt_source[index],
                             reg, "interrupt source", index);
                             reg, "interrupt source", index);
}
}
 
 
 
 
 
 
/* Spurious vector register: */
/* Spurious vector register: */
 
 
static unsigned
static unsigned
do_spurious_vector_register_read(device *me,
do_spurious_vector_register_read(device *me,
                                 hw_opic_device *opic)
                                 hw_opic_device *opic)
{
{
  unsigned long reg = opic->spurious_vector;
  unsigned long reg = opic->spurious_vector;
  DTRACE(opic, ("spurious vector register - read 0x%lx\n", reg));
  DTRACE(opic, ("spurious vector register - read 0x%lx\n", reg));
  return reg;
  return reg;
}
}
 
 
static void
static void
do_spurious_vector_register_write(device *me,
do_spurious_vector_register_write(device *me,
                                  hw_opic_device *opic,
                                  hw_opic_device *opic,
                                  unsigned reg)
                                  unsigned reg)
{
{
  reg &= 0xff; /* mask off invalid */
  reg &= 0xff; /* mask off invalid */
  DTRACE(opic, ("spurious vector register - write 0x%x\n", reg));
  DTRACE(opic, ("spurious vector register - write 0x%x\n", reg));
  opic->spurious_vector = reg;
  opic->spurious_vector = reg;
}
}
 
 
 
 
 
 
/* current task priority register: */
/* current task priority register: */
 
 
static unsigned
static unsigned
do_current_task_priority_register_N_read(device *me,
do_current_task_priority_register_N_read(device *me,
                                         hw_opic_device *opic,
                                         hw_opic_device *opic,
                                         int index)
                                         int index)
{
{
  opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index];
  opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index];
  unsigned reg;
  unsigned reg;
  ASSERT(index >= 0 && index < opic->nr_interrupt_destinations);
  ASSERT(index >= 0 && index < opic->nr_interrupt_destinations);
  reg = interrupt_destination->base_priority;
  reg = interrupt_destination->base_priority;
  DTRACE(opic, ("current task priority register %d - read 0x%x\n", index, reg));
  DTRACE(opic, ("current task priority register %d - read 0x%x\n", index, reg));
  return reg;
  return reg;
}
}
 
 
static void
static void
do_current_task_priority_register_N_write(device *me,
do_current_task_priority_register_N_write(device *me,
                                          hw_opic_device *opic,
                                          hw_opic_device *opic,
                                          int index,
                                          int index,
                                          unsigned reg)
                                          unsigned reg)
{
{
  opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index];
  opic_interrupt_destination *interrupt_destination = &opic->interrupt_destination[index];
  ASSERT(index >= 0 && index < opic->nr_interrupt_destinations);
  ASSERT(index >= 0 && index < opic->nr_interrupt_destinations);
  reg %= max_nr_task_priorities;
  reg %= max_nr_task_priorities;
  DTRACE(opic, ("current task priority register %d - write 0x%x\n", index, reg));
  DTRACE(opic, ("current task priority register %d - write 0x%x\n", index, reg));
  interrupt_destination->base_priority = reg;
  interrupt_destination->base_priority = reg;
}
}
 
 
 
 
 
 
/* Timer Frequency Reporting Register: */
/* Timer Frequency Reporting Register: */
 
 
static unsigned
static unsigned
do_timer_frequency_reporting_register_read(device *me,
do_timer_frequency_reporting_register_read(device *me,
                                           hw_opic_device *opic)
                                           hw_opic_device *opic)
{
{
  unsigned reg;
  unsigned reg;
  reg = opic->timer_frequency;
  reg = opic->timer_frequency;
  DTRACE(opic, ("timer frequency reporting register - read 0x%x\n", reg));
  DTRACE(opic, ("timer frequency reporting register - read 0x%x\n", reg));
  return reg;
  return reg;
}
}
 
 
static void
static void
do_timer_frequency_reporting_register_write(device *me,
do_timer_frequency_reporting_register_write(device *me,
                                            hw_opic_device *opic,
                                            hw_opic_device *opic,
                                            unsigned reg)
                                            unsigned reg)
{
{
  DTRACE(opic, ("timer frequency reporting register - write 0x%x\n", reg));
  DTRACE(opic, ("timer frequency reporting register - write 0x%x\n", reg));
  opic->timer_frequency = reg;
  opic->timer_frequency = reg;
}
}
 
 
 
 
/* timer registers: */
/* timer registers: */
 
 
static unsigned
static unsigned
do_timer_N_current_count_register_read(device *me,
do_timer_N_current_count_register_read(device *me,
                                       hw_opic_device *opic,
                                       hw_opic_device *opic,
                                       int index)
                                       int index)
{
{
  opic_timer *timer = &opic->timer[index];
  opic_timer *timer = &opic->timer[index];
  unsigned reg;
  unsigned reg;
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  if (timer->inhibited)
  if (timer->inhibited)
    reg = timer->count; /* stalled value */
    reg = timer->count; /* stalled value */
  else
  else
    reg = timer->count - device_event_queue_time(me); /* time remaining */
    reg = timer->count - device_event_queue_time(me); /* time remaining */
  DTRACE(opic, ("timer %d current count register - read 0x%x\n", index, reg));
  DTRACE(opic, ("timer %d current count register - read 0x%x\n", index, reg));
  return reg;
  return reg;
}
}
 
 
 
 
static unsigned
static unsigned
do_timer_N_base_count_register_read(device *me,
do_timer_N_base_count_register_read(device *me,
                                    hw_opic_device *opic,
                                    hw_opic_device *opic,
                                    int index)
                                    int index)
{
{
  opic_timer *timer = &opic->timer[index];
  opic_timer *timer = &opic->timer[index];
  unsigned reg;
  unsigned reg;
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  reg = timer->base_count;
  reg = timer->base_count;
  DTRACE(opic, ("timer %d base count register - read 0x%x\n", index, reg));
  DTRACE(opic, ("timer %d base count register - read 0x%x\n", index, reg));
  return reg;
  return reg;
}
}
 
 
 
 
static void
static void
timer_event(void *data)
timer_event(void *data)
{
{
  opic_timer *timer = data;
  opic_timer *timer = data;
  device *me = timer->me;
  device *me = timer->me;
  if (timer->inhibited)
  if (timer->inhibited)
    device_error(timer->me, "internal-error - timer event occured when timer %d inhibited",
    device_error(timer->me, "internal-error - timer event occured when timer %d inhibited",
                 timer->nr);
                 timer->nr);
  handle_interrupt(timer->me, timer->opic, timer->interrupt_source, 1);
  handle_interrupt(timer->me, timer->opic, timer->interrupt_source, 1);
  timer->timeout_event = device_event_queue_schedule(me, timer->base_count,
  timer->timeout_event = device_event_queue_schedule(me, timer->base_count,
                                                     timer_event, timer);
                                                     timer_event, timer);
  DTRACE(opic, ("timer %d - interrupt at %ld, next at %d\n",
  DTRACE(opic, ("timer %d - interrupt at %ld, next at %d\n",
                timer->nr, (long)device_event_queue_time(me), timer->base_count));
                timer->nr, (long)device_event_queue_time(me), timer->base_count));
}
}
 
 
 
 
static void
static void
do_timer_N_base_count_register_write(device *me,
do_timer_N_base_count_register_write(device *me,
                                     hw_opic_device *opic,
                                     hw_opic_device *opic,
                                     int index,
                                     int index,
                                     unsigned reg)
                                     unsigned reg)
{
{
  opic_timer *timer = &opic->timer[index];
  opic_timer *timer = &opic->timer[index];
  int inhibit;
  int inhibit;
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  inhibit = reg & 0x80000000;
  inhibit = reg & 0x80000000;
  if (timer->inhibited && !inhibit) {
  if (timer->inhibited && !inhibit) {
    timer->inhibited = 0;
    timer->inhibited = 0;
    if (timer->timeout_event != NULL)
    if (timer->timeout_event != NULL)
      device_event_queue_deschedule(me, timer->timeout_event);
      device_event_queue_deschedule(me, timer->timeout_event);
    timer->count = device_event_queue_time(me) + reg;
    timer->count = device_event_queue_time(me) + reg;
    timer->base_count = reg;
    timer->base_count = reg;
    timer->timeout_event = device_event_queue_schedule(me, timer->base_count,
    timer->timeout_event = device_event_queue_schedule(me, timer->base_count,
                                                       timer_event, (void*)timer);
                                                       timer_event, (void*)timer);
    DTRACE(opic, ("timer %d base count register - write 0x%x - timer started\n",
    DTRACE(opic, ("timer %d base count register - write 0x%x - timer started\n",
                  index, reg));
                  index, reg));
  }
  }
  else if (!timer->inhibited && inhibit) {
  else if (!timer->inhibited && inhibit) {
    if (timer->timeout_event != NULL)
    if (timer->timeout_event != NULL)
      device_event_queue_deschedule(me, timer->timeout_event);
      device_event_queue_deschedule(me, timer->timeout_event);
    timer->count = timer->count - device_event_queue_time(me);
    timer->count = timer->count - device_event_queue_time(me);
    timer->inhibited = 1;
    timer->inhibited = 1;
    timer->base_count = reg;
    timer->base_count = reg;
    DTRACE(opic, ("timer %d base count register - write 0x%x - timer stopped\n",
    DTRACE(opic, ("timer %d base count register - write 0x%x - timer stopped\n",
                  index, reg));
                  index, reg));
  }
  }
  else {
  else {
    ASSERT((timer->inhibited && inhibit) || (!timer->inhibited && !inhibit));
    ASSERT((timer->inhibited && inhibit) || (!timer->inhibited && !inhibit));
    DTRACE(opic, ("timer %d base count register - write 0x%x\n", index, reg));
    DTRACE(opic, ("timer %d base count register - write 0x%x\n", index, reg));
    timer->base_count = reg;
    timer->base_count = reg;
  }
  }
}
}
 
 
 
 
static unsigned
static unsigned
do_timer_N_vector_priority_register_read(device *me,
do_timer_N_vector_priority_register_read(device *me,
                                         hw_opic_device *opic,
                                         hw_opic_device *opic,
                                         int index)
                                         int index)
{
{
  unsigned reg;
  unsigned reg;
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  reg = read_vector_priority_register(me, opic,
  reg = read_vector_priority_register(me, opic,
                                      &opic->timer_interrupt_source[index],
                                      &opic->timer_interrupt_source[index],
                                      "timer", index);
                                      "timer", index);
  return reg;
  return reg;
}
}
 
 
static void
static void
do_timer_N_vector_priority_register_write(device *me,
do_timer_N_vector_priority_register_write(device *me,
                                          hw_opic_device *opic,
                                          hw_opic_device *opic,
                                          int index,
                                          int index,
                                          unsigned reg)
                                          unsigned reg)
{
{
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  reg &= ~isu_level_triggered_bit; /* force edge trigger */
  reg &= ~isu_level_triggered_bit; /* force edge trigger */
  reg |= isu_positive_polarity_bit; /* force rising (positive) edge */
  reg |= isu_positive_polarity_bit; /* force rising (positive) edge */
  reg |= isu_multicast_bit; /* force multicast */
  reg |= isu_multicast_bit; /* force multicast */
  write_vector_priority_register(me, opic,
  write_vector_priority_register(me, opic,
                                 &opic->timer_interrupt_source[index],
                                 &opic->timer_interrupt_source[index],
                                 reg, "timer", index);
                                 reg, "timer", index);
}
}
 
 
 
 
static unsigned
static unsigned
do_timer_N_destination_register_read(device *me,
do_timer_N_destination_register_read(device *me,
                                     hw_opic_device *opic,
                                     hw_opic_device *opic,
                                     int index)
                                     int index)
{
{
  unsigned reg;
  unsigned reg;
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  reg = read_destination_register(me, opic, &opic->timer_interrupt_source[index],
  reg = read_destination_register(me, opic, &opic->timer_interrupt_source[index],
                                  "timer", index);
                                  "timer", index);
  return reg;
  return reg;
}
}
 
 
static void
static void
do_timer_N_destination_register_write(device *me,
do_timer_N_destination_register_write(device *me,
                                      hw_opic_device *opic,
                                      hw_opic_device *opic,
                                      int index,
                                      int index,
                                      unsigned reg)
                                      unsigned reg)
{
{
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  ASSERT(index >= 0 && index < opic->nr_timer_interrupts);
  write_destination_register(me, opic, &opic->timer_interrupt_source[index],
  write_destination_register(me, opic, &opic->timer_interrupt_source[index],
                             reg, "timer", index);
                             reg, "timer", index);
}
}
 
 
 
 
/* IPI registers */
/* IPI registers */
 
 
static unsigned
static unsigned
do_ipi_N_vector_priority_register_read(device *me,
do_ipi_N_vector_priority_register_read(device *me,
                                       hw_opic_device *opic,
                                       hw_opic_device *opic,
                                       int index)
                                       int index)
{
{
  unsigned reg;
  unsigned reg;
  ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
  ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
  reg = read_vector_priority_register(me, opic,
  reg = read_vector_priority_register(me, opic,
                                      &opic->interprocessor_interrupt_source[index],
                                      &opic->interprocessor_interrupt_source[index],
                                      "ipi", index);
                                      "ipi", index);
  return reg;
  return reg;
}
}
 
 
static void
static void
do_ipi_N_vector_priority_register_write(device *me,
do_ipi_N_vector_priority_register_write(device *me,
                                        hw_opic_device *opic,
                                        hw_opic_device *opic,
                                        int index,
                                        int index,
                                        unsigned reg)
                                        unsigned reg)
{
{
  ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
  ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
  reg &= ~isu_level_triggered_bit; /* force edge trigger */
  reg &= ~isu_level_triggered_bit; /* force edge trigger */
  reg |= isu_positive_polarity_bit; /* force rising (positive) edge */
  reg |= isu_positive_polarity_bit; /* force rising (positive) edge */
  reg |= isu_multicast_bit; /* force a multicast source */
  reg |= isu_multicast_bit; /* force a multicast source */
  write_vector_priority_register(me, opic,
  write_vector_priority_register(me, opic,
                                 &opic->interprocessor_interrupt_source[index],
                                 &opic->interprocessor_interrupt_source[index],
                                 reg, "ipi", index);
                                 reg, "ipi", index);
}
}
 
 
static void
static void
do_ipi_N_dispatch_register_write(device *me,
do_ipi_N_dispatch_register_write(device *me,
                                 hw_opic_device *opic,
                                 hw_opic_device *opic,
                                 int index,
                                 int index,
                                 unsigned reg)
                                 unsigned reg)
{
{
  opic_interrupt_source *source = &opic->interprocessor_interrupt_source[index];
  opic_interrupt_source *source = &opic->interprocessor_interrupt_source[index];
  ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
  ASSERT(index >= 0 && index < opic->nr_interprocessor_interrupts);
  DTRACE(opic, ("ipi %d interrupt dispatch register - write 0x%x\n", index, reg));
  DTRACE(opic, ("ipi %d interrupt dispatch register - write 0x%x\n", index, reg));
  source->destination = reg;
  source->destination = reg;
  handle_interrupt(me, opic, source, 1);
  handle_interrupt(me, opic, source, 1);
}
}
 
 
 
 
/* vendor and other global registers */
/* vendor and other global registers */
 
 
static unsigned
static unsigned
do_vendor_identification_register_read(device *me,
do_vendor_identification_register_read(device *me,
                                       hw_opic_device *opic)
                                       hw_opic_device *opic)
{
{
  unsigned reg;
  unsigned reg;
  reg = opic->vendor_identification;
  reg = opic->vendor_identification;
  DTRACE(opic, ("vendor identification register - read 0x%x\n", reg));
  DTRACE(opic, ("vendor identification register - read 0x%x\n", reg));
  return reg;
  return reg;
}
}
 
 
static unsigned
static unsigned
do_feature_reporting_register_N_read(device *me,
do_feature_reporting_register_N_read(device *me,
                                     hw_opic_device *opic,
                                     hw_opic_device *opic,
                                     int index)
                                     int index)
{
{
  unsigned reg = 0;
  unsigned reg = 0;
  ASSERT(index == 0);
  ASSERT(index == 0);
  switch (index) {
  switch (index) {
  case 0:
  case 0:
    reg |= (opic->nr_external_interrupts << 16);
    reg |= (opic->nr_external_interrupts << 16);
    reg |= (opic->nr_interrupt_destinations << 8);
    reg |= (opic->nr_interrupt_destinations << 8);
    reg |= (2/*version 1.2*/);
    reg |= (2/*version 1.2*/);
    break;
    break;
  }
  }
  DTRACE(opic, ("feature reporting register %d - read 0x%x\n", index, reg));
  DTRACE(opic, ("feature reporting register %d - read 0x%x\n", index, reg));
  return reg;
  return reg;
}
}
 
 
static unsigned
static unsigned
do_global_configuration_register_N_read(device *me,
do_global_configuration_register_N_read(device *me,
                                        hw_opic_device *opic,
                                        hw_opic_device *opic,
                                        int index)
                                        int index)
{
{
  unsigned reg = 0;
  unsigned reg = 0;
  ASSERT(index == 0);
  ASSERT(index == 0);
  switch (index) {
  switch (index) {
  case 0:
  case 0:
    reg |= gcr0_8259_bit; /* hardwire 8259 disabled */
    reg |= gcr0_8259_bit; /* hardwire 8259 disabled */
    break;
    break;
  }
  }
  DTRACE(opic, ("global configuration register %d - read 0x%x\n", index, reg));
  DTRACE(opic, ("global configuration register %d - read 0x%x\n", index, reg));
  return reg;
  return reg;
}
}
 
 
static void
static void
do_global_configuration_register_N_write(device *me,
do_global_configuration_register_N_write(device *me,
                                         hw_opic_device *opic,
                                         hw_opic_device *opic,
                                         int index,
                                         int index,
                                         unsigned reg)
                                         unsigned reg)
{
{
  ASSERT(index == 0);
  ASSERT(index == 0);
  if (reg & gcr0_reset_bit) {
  if (reg & gcr0_reset_bit) {
    DTRACE(opic, ("global configuration register %d - write 0x%x - reseting opic\n", index, reg));
    DTRACE(opic, ("global configuration register %d - write 0x%x - reseting opic\n", index, reg));
    hw_opic_init_data(me);
    hw_opic_init_data(me);
  }
  }
  if (!(reg & gcr0_8259_bit)) {
  if (!(reg & gcr0_8259_bit)) {
    DTRACE(opic, ("global configuration register %d - write 0x%x - ignoring 8259 enable\n", index, reg));
    DTRACE(opic, ("global configuration register %d - write 0x%x - ignoring 8259 enable\n", index, reg));
  }
  }
}
}
 
 
 
 
 
 
/* register read-write */
/* register read-write */
 
 
static unsigned
static unsigned
hw_opic_io_read_buffer(device *me,
hw_opic_io_read_buffer(device *me,
                       void *dest,
                       void *dest,
                       int space,
                       int space,
                       unsigned_word addr,
                       unsigned_word addr,
                       unsigned nr_bytes,
                       unsigned nr_bytes,
                       cpu *processor,
                       cpu *processor,
                       unsigned_word cia)
                       unsigned_word cia)
{
{
  hw_opic_device *opic = (hw_opic_device*)device_data(me);
  hw_opic_device *opic = (hw_opic_device*)device_data(me);
  opic_register type;
  opic_register type;
  int index;
  int index;
  decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index);
  decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index);
  if (type == invalid_opic_register) {
  if (type == invalid_opic_register) {
    device_error(me, "invalid opic read access to %d:0x%lx (%d bytes)",
    device_error(me, "invalid opic read access to %d:0x%lx (%d bytes)",
                 space, (unsigned long)addr, nr_bytes);
                 space, (unsigned long)addr, nr_bytes);
  }
  }
  else {
  else {
    unsigned reg;
    unsigned reg;
    switch (type) {
    switch (type) {
    case processor_init_register:
    case processor_init_register:
      reg = do_processor_init_register_read(me, opic);
      reg = do_processor_init_register_read(me, opic);
      break;
      break;
    case interrupt_source_N_vector_priority_register:
    case interrupt_source_N_vector_priority_register:
      reg = do_interrupt_source_N_vector_priority_register_read(me, opic, index);
      reg = do_interrupt_source_N_vector_priority_register_read(me, opic, index);
      break;
      break;
    case interrupt_source_N_destination_register:
    case interrupt_source_N_destination_register:
      reg = do_interrupt_source_N_destination_register_read(me, opic, index);
      reg = do_interrupt_source_N_destination_register_read(me, opic, index);
      break;
      break;
    case interrupt_acknowledge_register_N:
    case interrupt_acknowledge_register_N:
      reg = do_interrupt_acknowledge_register_N_read(me, opic, index);
      reg = do_interrupt_acknowledge_register_N_read(me, opic, index);
      break;
      break;
    case spurious_vector_register:
    case spurious_vector_register:
      reg = do_spurious_vector_register_read(me, opic);
      reg = do_spurious_vector_register_read(me, opic);
      break;
      break;
    case current_task_priority_register_N:
    case current_task_priority_register_N:
      reg = do_current_task_priority_register_N_read(me, opic, index);
      reg = do_current_task_priority_register_N_read(me, opic, index);
      break;
      break;
    case timer_frequency_reporting_register:
    case timer_frequency_reporting_register:
      reg = do_timer_frequency_reporting_register_read(me, opic);
      reg = do_timer_frequency_reporting_register_read(me, opic);
      break;
      break;
    case timer_N_current_count_register:
    case timer_N_current_count_register:
      reg = do_timer_N_current_count_register_read(me, opic, index);
      reg = do_timer_N_current_count_register_read(me, opic, index);
      break;
      break;
    case timer_N_base_count_register:
    case timer_N_base_count_register:
      reg = do_timer_N_base_count_register_read(me, opic, index);
      reg = do_timer_N_base_count_register_read(me, opic, index);
      break;
      break;
    case timer_N_vector_priority_register:
    case timer_N_vector_priority_register:
      reg = do_timer_N_vector_priority_register_read(me, opic, index);
      reg = do_timer_N_vector_priority_register_read(me, opic, index);
      break;
      break;
    case timer_N_destination_register:
    case timer_N_destination_register:
      reg = do_timer_N_destination_register_read(me, opic, index);
      reg = do_timer_N_destination_register_read(me, opic, index);
      break;
      break;
    case ipi_N_vector_priority_register:
    case ipi_N_vector_priority_register:
      reg = do_ipi_N_vector_priority_register_read(me, opic, index);
      reg = do_ipi_N_vector_priority_register_read(me, opic, index);
      break;
      break;
    case feature_reporting_register_N:
    case feature_reporting_register_N:
      reg = do_feature_reporting_register_N_read(me, opic, index);
      reg = do_feature_reporting_register_N_read(me, opic, index);
      break;
      break;
    case global_configuration_register_N:
    case global_configuration_register_N:
      reg = do_global_configuration_register_N_read(me, opic, index);
      reg = do_global_configuration_register_N_read(me, opic, index);
      break;
      break;
    case vendor_identification_register:
    case vendor_identification_register:
      reg = do_vendor_identification_register_read(me, opic);
      reg = do_vendor_identification_register_read(me, opic);
      break;
      break;
    default:
    default:
      reg = 0;
      reg = 0;
      device_error(me, "unimplemented read of register %s[%d]",
      device_error(me, "unimplemented read of register %s[%d]",
                   opic_register_name(type), index);
                   opic_register_name(type), index);
    }
    }
    *(unsigned_4*)dest = H2LE_4(reg);
    *(unsigned_4*)dest = H2LE_4(reg);
  }
  }
  return nr_bytes;
  return nr_bytes;
}
}
 
 
 
 
static unsigned
static unsigned
hw_opic_io_write_buffer(device *me,
hw_opic_io_write_buffer(device *me,
                        const void *source,
                        const void *source,
                        int space,
                        int space,
                        unsigned_word addr,
                        unsigned_word addr,
                        unsigned nr_bytes,
                        unsigned nr_bytes,
                        cpu *processor,
                        cpu *processor,
                        unsigned_word cia)
                        unsigned_word cia)
{
{
  hw_opic_device *opic = (hw_opic_device*)device_data(me);
  hw_opic_device *opic = (hw_opic_device*)device_data(me);
  opic_register type;
  opic_register type;
  int index;
  int index;
  decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index);
  decode_opic_address(me, opic, space, addr, nr_bytes, &type, &index);
  if (type == invalid_opic_register) {
  if (type == invalid_opic_register) {
    device_error(me, "invalid opic write access to %d:0x%lx (%d bytes)",
    device_error(me, "invalid opic write access to %d:0x%lx (%d bytes)",
                 space, (unsigned long)addr, nr_bytes);
                 space, (unsigned long)addr, nr_bytes);
  }
  }
  else {
  else {
    unsigned reg = LE2H_4(*(unsigned_4*)source);
    unsigned reg = LE2H_4(*(unsigned_4*)source);
    switch (type) {
    switch (type) {
    case processor_init_register:
    case processor_init_register:
      do_processor_init_register_write(me, opic, reg);
      do_processor_init_register_write(me, opic, reg);
      break;
      break;
    case interrupt_source_N_vector_priority_register:
    case interrupt_source_N_vector_priority_register:
      do_interrupt_source_N_vector_priority_register_write(me, opic, index, reg);
      do_interrupt_source_N_vector_priority_register_write(me, opic, index, reg);
      break;
      break;
    case interrupt_source_N_destination_register:
    case interrupt_source_N_destination_register:
      do_interrupt_source_N_destination_register_write(me, opic, index, reg);
      do_interrupt_source_N_destination_register_write(me, opic, index, reg);
      break;
      break;
    case end_of_interrupt_register_N:
    case end_of_interrupt_register_N:
      do_end_of_interrupt_register_N_write(me, opic, index, reg);
      do_end_of_interrupt_register_N_write(me, opic, index, reg);
      break;
      break;
    case spurious_vector_register:
    case spurious_vector_register:
      do_spurious_vector_register_write(me, opic, reg);
      do_spurious_vector_register_write(me, opic, reg);
      break;
      break;
    case current_task_priority_register_N:
    case current_task_priority_register_N:
      do_current_task_priority_register_N_write(me, opic, index, reg);
      do_current_task_priority_register_N_write(me, opic, index, reg);
      break;
      break;
    case timer_frequency_reporting_register:
    case timer_frequency_reporting_register:
      do_timer_frequency_reporting_register_write(me, opic, reg);
      do_timer_frequency_reporting_register_write(me, opic, reg);
      break;
      break;
    case timer_N_base_count_register:
    case timer_N_base_count_register:
      do_timer_N_base_count_register_write(me, opic, index, reg);
      do_timer_N_base_count_register_write(me, opic, index, reg);
      break;
      break;
    case timer_N_vector_priority_register:
    case timer_N_vector_priority_register:
      do_timer_N_vector_priority_register_write(me, opic, index, reg);
      do_timer_N_vector_priority_register_write(me, opic, index, reg);
      break;
      break;
    case timer_N_destination_register:
    case timer_N_destination_register:
      do_timer_N_destination_register_write(me, opic, index, reg);
      do_timer_N_destination_register_write(me, opic, index, reg);
      break;
      break;
    case ipi_N_dispatch_register:
    case ipi_N_dispatch_register:
      do_ipi_N_dispatch_register_write(me, opic, index, reg);
      do_ipi_N_dispatch_register_write(me, opic, index, reg);
      break;
      break;
    case ipi_N_vector_priority_register:
    case ipi_N_vector_priority_register:
      do_ipi_N_vector_priority_register_write(me, opic, index, reg);
      do_ipi_N_vector_priority_register_write(me, opic, index, reg);
      break;
      break;
    case global_configuration_register_N:
    case global_configuration_register_N:
      do_global_configuration_register_N_write(me, opic, index, reg);
      do_global_configuration_register_N_write(me, opic, index, reg);
      break;
      break;
    default:
    default:
      device_error(me, "unimplemented write to register %s[%d]",
      device_error(me, "unimplemented write to register %s[%d]",
                   opic_register_name(type), index);
                   opic_register_name(type), index);
    }
    }
  }
  }
  return nr_bytes;
  return nr_bytes;
}
}
 
 
 
 
static void
static void
hw_opic_interrupt_event(device *me,
hw_opic_interrupt_event(device *me,
                        int my_port,
                        int my_port,
                        device *source,
                        device *source,
                        int source_port,
                        int source_port,
                        int level,
                        int level,
                        cpu *processor,
                        cpu *processor,
                        unsigned_word cia)
                        unsigned_word cia)
{
{
  hw_opic_device *opic = (hw_opic_device*)device_data(me);
  hw_opic_device *opic = (hw_opic_device*)device_data(me);
 
 
  int isb;
  int isb;
  int src_nr = 0;
  int src_nr = 0;
 
 
  /* find the corresponding internal input port */
  /* find the corresponding internal input port */
  for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
  for (isb = 0; isb < opic->nr_isu_blocks; isb++) {
    if (my_port >= opic->isu_block[isb].int_number
    if (my_port >= opic->isu_block[isb].int_number
        && my_port < opic->isu_block[isb].int_number + opic->isu_block[isb].range) {
        && my_port < opic->isu_block[isb].int_number + opic->isu_block[isb].range) {
      src_nr += my_port - opic->isu_block[isb].int_number;
      src_nr += my_port - opic->isu_block[isb].int_number;
      break;
      break;
    }
    }
    else
    else
      src_nr += opic->isu_block[isb].range;
      src_nr += opic->isu_block[isb].range;
  }
  }
  if (isb == opic->nr_isu_blocks)
  if (isb == opic->nr_isu_blocks)
    device_error(me, "interrupt %d out of range", my_port);
    device_error(me, "interrupt %d out of range", my_port);
  DTRACE(opic, ("external-interrupt %d, internal %d, level %d\n",
  DTRACE(opic, ("external-interrupt %d, internal %d, level %d\n",
                my_port, src_nr, level));
                my_port, src_nr, level));
 
 
  /* pass it on */
  /* pass it on */
  ASSERT(src_nr >= 0 && src_nr < opic->nr_external_interrupts);
  ASSERT(src_nr >= 0 && src_nr < opic->nr_external_interrupts);
  handle_interrupt(me, opic, &opic->external_interrupt_source[src_nr], level);
  handle_interrupt(me, opic, &opic->external_interrupt_source[src_nr], level);
}
}
 
 
 
 
static const device_interrupt_port_descriptor hw_opic_interrupt_ports[] = {
static const device_interrupt_port_descriptor hw_opic_interrupt_ports[] = {
  { "irq", 0, max_nr_interrupt_sources, input_port, },
  { "irq", 0, max_nr_interrupt_sources, input_port, },
  { "intr", 0, max_nr_interrupt_destinations, output_port, },
  { "intr", 0, max_nr_interrupt_destinations, output_port, },
  { "init", max_nr_interrupt_destinations, max_nr_interrupt_destinations, output_port, },
  { "init", max_nr_interrupt_destinations, max_nr_interrupt_destinations, output_port, },
  { NULL }
  { NULL }
};
};
 
 
 
 
static device_callbacks const hw_opic_callbacks = {
static device_callbacks const hw_opic_callbacks = {
  { generic_device_init_address,
  { generic_device_init_address,
    hw_opic_init_data },
    hw_opic_init_data },
  { NULL, }, /* address */
  { NULL, }, /* address */
  { hw_opic_io_read_buffer,
  { hw_opic_io_read_buffer,
    hw_opic_io_write_buffer }, /* IO */
    hw_opic_io_write_buffer }, /* IO */
  { NULL, }, /* DMA */
  { NULL, }, /* DMA */
  { hw_opic_interrupt_event, NULL, hw_opic_interrupt_ports }, /* interrupt */
  { hw_opic_interrupt_event, NULL, hw_opic_interrupt_ports }, /* interrupt */
  { NULL, }, /* unit */
  { NULL, }, /* unit */
  NULL, /* instance */
  NULL, /* instance */
};
};
 
 
static void *
static void *
hw_opic_create(const char *name,
hw_opic_create(const char *name,
               const device_unit *unit_address,
               const device_unit *unit_address,
               const char *args)
               const char *args)
{
{
  hw_opic_device *opic = ZALLOC(hw_opic_device);
  hw_opic_device *opic = ZALLOC(hw_opic_device);
  return opic;
  return opic;
}
}
 
 
 
 
 
 
const device_descriptor hw_opic_device_descriptor[] = {
const device_descriptor hw_opic_device_descriptor[] = {
  { "opic", hw_opic_create, &hw_opic_callbacks },
  { "opic", hw_opic_create, &hw_opic_callbacks },
  { NULL },
  { NULL },
};
};
 
 
#endif /* _HW_OPIC_C_ */
#endif /* _HW_OPIC_C_ */
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.