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