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