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