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