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