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