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