📄 hw_opic.c
字号:
opic->nr_interprocessor_interrupts = 4; opic->nr_timer_interrupts = 4; /* create space for the interrupt source registers */ if (opic->interrupt_source != NULL) { memset(opic->interrupt_source, 0, opic->sizeof_interrupt_source); } else { opic->nr_interrupt_sources = (opic->nr_external_interrupts + opic->nr_interprocessor_interrupts + opic->nr_timer_interrupts); if (opic->nr_interrupt_sources > max_nr_interrupt_sources) device_error(me, "number of interrupt sources exceeded"); opic->sizeof_interrupt_source = (sizeof(opic_interrupt_source) * opic->nr_interrupt_sources); opic->interrupt_source = zalloc(opic->sizeof_interrupt_source); opic->external_interrupt_source = opic->interrupt_source; opic->interprocessor_interrupt_source = (opic->external_interrupt_source + opic->nr_external_interrupts); opic->timer_interrupt_source = (opic->interprocessor_interrupt_source + opic->nr_interprocessor_interrupts); } for (i = 0; i < opic->nr_interrupt_sources; i++) { opic_interrupt_source *source = &opic->interrupt_source[i]; source->nr = i; source->is_masked = isu_mask_bit; } DTRACE(opic, ("interrupt sources - external %d, timer %d, ipi %d, total %d\n", opic->nr_external_interrupts, opic->nr_timer_interrupts, opic->nr_interprocessor_interrupts, opic->nr_interrupt_sources)); /* timers or interprocessor interrupts */ if (opic->timer != NULL) memset(opic->timer, 0, opic->sizeof_timer); else { opic->nr_timer_interrupts = 4; opic->sizeof_timer = sizeof(opic_timer) * opic->nr_timer_interrupts; opic->timer = zalloc(opic->sizeof_timer); } for (i = 0; i < opic->nr_timer_interrupts; i++) { opic_timer *timer = &opic->timer[i]; timer->nr = i; timer->me = me; timer->opic = opic; timer->inhibited = 1; timer->interrupt_source = &opic->timer_interrupt_source[i]; } if (device_find_property(me, "timer-frequency")) opic->timer_frequency = device_find_integer_property(me, "timer-frequency"); else opic->timer_frequency = 1; /* create space for the interrupt destination registers */ if (opic->interrupt_destination != NULL) { memset(opic->interrupt_destination, 0, opic->sizeof_interrupt_destination); } else { opic->nr_interrupt_destinations = tree_find_integer_property(me, "/openprom/options/smp"); opic->sizeof_interrupt_destination = (sizeof(opic_interrupt_destination) * opic->nr_interrupt_destinations); opic->interrupt_destination = zalloc(opic->sizeof_interrupt_destination); if (opic->nr_interrupt_destinations > max_nr_interrupt_destinations) device_error(me, "number of interrupt destinations exceeded"); } for (i = 0; i < opic->nr_interrupt_destinations; i++) { opic_interrupt_destination *dest = &opic->interrupt_destination[i]; dest->bit = (1 << i); dest->nr = i; dest->init_port = (device_interrupt_decode(me, "init0", output_port) + i); dest->intr_port = (device_interrupt_decode(me, "intr0", output_port) + i); dest->base_priority = max_nr_task_priorities - 1; } DTRACE(opic, ("interrupt destinations - total %d\n", (int)opic->nr_interrupt_destinations)); /* verify and print out the ISU's */ for (isb = 0; isb < opic->nr_isu_blocks; isb++) { unsigned correct_size; if ((opic->isu_block[isb].address % opic_alignment) != 0) device_error(me, "interrupt source unit %d address not aligned to %d byte boundary", isb, opic_alignment); correct_size = opic->isu_block[isb].range * sizeof_isu_register_block; if (opic->isu_block[isb].size != correct_size) device_error(me, "interrupt source unit %d (reg %d) has an incorrect size, should be 0x%x", isb, opic->isu_block[isb].reg, correct_size); DTRACE(opic, ("interrupt source unit block %ld - address %d:0x%lx, size 0x%lx, int-number %ld, range %ld\n", (long)isb, (int)opic->isu_block[isb].space, (unsigned long)opic->isu_block[isb].address, (unsigned long)opic->isu_block[isb].size, (long)opic->isu_block[isb].int_number, (long)opic->isu_block[isb].range)); } /* verify and print out the IDU */ { unsigned correct_size; unsigned alternate_size; if ((opic->idu.address % opic_alignment) != 0) device_error(me, "interrupt delivery unit not aligned to %d byte boundary", opic_alignment); correct_size = (idu_per_processor_register_base + (sizeof_idu_per_processor_register_block * opic->nr_interrupt_destinations)); alternate_size = (idu_per_processor_register_base + (sizeof_idu_per_processor_register_block * max_nr_interrupt_destinations)); if (opic->idu.size != correct_size && opic->idu.size != alternate_size) device_error(me, "interrupt delivery unit has incorrect size, should be 0x%x or 0x%x", correct_size, alternate_size); DTRACE(opic, ("interrupt delivery unit - address %d:0x%lx, size 0x%lx\n", (int)opic->idu.space, (unsigned long)opic->idu.address, (unsigned long)opic->idu.size)); } /* initialize the init interrupts */ opic->init = 0; /* vendor ident */ if (device_find_property(me, "vendor-identification") != NULL) opic->vendor_identification = device_find_integer_property(me, "vendor-identification"); else opic->vendor_identification = 0; /* misc registers */ opic->spurious_vector = 0xff;}/* interrupt related actions */static voidassert_interrupt(device *me, hw_opic_device *opic, opic_interrupt_destination *dest){ ASSERT(dest >= opic->interrupt_destination); ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations); DTRACE(opic, ("assert interrupt - intr port %d\n", dest->intr_port)); device_interrupt_event(me, dest->intr_port, 1, NULL, 0);}static voidnegate_interrupt(device *me, hw_opic_device *opic, opic_interrupt_destination *dest){ ASSERT(dest >= opic->interrupt_destination); ASSERT(dest < opic->interrupt_destination + opic->nr_interrupt_destinations); DTRACE(opic, ("negate interrupt - intr port %d\n", dest->intr_port)); device_interrupt_event(me, dest->intr_port, 0, NULL, 0);}static intcan_deliver(device *me, opic_interrupt_source *source, opic_interrupt_destination *dest){ return (source != NULL && dest != NULL && source->priority > dest->base_priority && (dest->current_in_service == NULL || source->priority > dest->current_in_service->priority));}static unsigneddeliver_pending(device *me, hw_opic_device *opic, opic_interrupt_destination *dest){ ASSERT(can_deliver(me, dest->current_pending, dest)); dest->current_in_service = dest->current_pending; dest->current_in_service->in_service |= dest->bit; if (!dest->current_pending->is_level_triggered) { if (dest->current_pending->is_multicast) dest->current_pending->pending &= ~dest->bit; else dest->current_pending->pending = 0; } dest->current_pending = NULL; negate_interrupt(me, opic, dest); return dest->current_in_service->vector;}typedef enum { pending_interrupt, in_service_interrupt,} interrupt_class;static opic_interrupt_source *find_interrupt_for_dest(device *me, hw_opic_device *opic, opic_interrupt_destination *dest, interrupt_class class){ int i; opic_interrupt_source *pending = NULL; for (i = 0; i < opic->nr_interrupt_sources; i++) { opic_interrupt_source *src = &opic->interrupt_source[i]; /* is this a potential hit? */ switch (class) { case in_service_interrupt: if ((src->in_service & dest->bit) == 0) continue; break; case pending_interrupt: if ((src->pending & dest->bit) == 0) continue; break; } /* see if it is the highest priority */ if (pending == NULL) pending = src; else if (src->priority > pending->priority) pending = src; } return pending;}static opic_interrupt_destination *find_lowest_dest(device *me, hw_opic_device *opic, opic_interrupt_source *src){ int i; opic_interrupt_destination *lowest = NULL; for (i = 0; i < opic->nr_interrupt_destinations; i++) { opic_interrupt_destination *dest = &opic->interrupt_destination[i]; if (src->destination & dest->bit) { if (dest->base_priority < src->priority) { if (lowest == NULL) lowest = dest; else if (lowest->base_priority > dest->base_priority) lowest = dest; else if (lowest->current_in_service != NULL && dest->current_in_service == NULL) lowest = dest; /* not doing anything */ else if (lowest->current_in_service != NULL && dest->current_in_service != NULL && (lowest->current_in_service->priority > dest->current_in_service->priority)) lowest = dest; /* less urgent */ /* FIXME - need to be more fair */ } } } return lowest;}static voidhandle_interrupt(device *me, hw_opic_device *opic, opic_interrupt_source *src, int asserted){ if (src->is_masked) { DTRACE(opic, ("interrupt %d - ignore masked\n", src->nr)); } else if (src->is_multicast) { /* always try to deliver multicast interrupts - just easier */ int i; ASSERT(!src->is_level_triggered); ASSERT(src->is_positive_polarity); ASSERT(asserted); for (i = 0; i < opic->nr_interrupt_destinations; i++) { opic_interrupt_destination *dest = &opic->interrupt_destination[i]; if (src->destination & dest->bit) { if (src->pending & dest->bit) { DTRACE(opic, ("interrupt %d - multicast still pending to %d\n", src->nr, dest->nr)); } else if (can_deliver(me, src, dest)) { dest->current_pending = src; src->pending |= dest->bit; assert_interrupt(me, opic, dest); DTRACE(opic, ("interrupt %d - multicast to %d\n", src->nr, dest->nr)); } else { src->pending |= dest->bit; DTRACE(opic, ("interrupt %d - multicast pending to %d\n", src->nr, dest->nr)); } } } } else if (src->is_level_triggered && src->is_positive_polarity && !asserted) { if (src->pending) DTRACE(opic, ("interrupt %d - ignore withdrawn (active high)\n", src->nr)); else DTRACE(opic, ("interrupt %d - ignore low level (active high)\n", src->nr)); ASSERT(!src->is_multicast); src->pending = 0; } else if (src->is_level_triggered && !src->is_positive_polarity && asserted) { if (src->pending) DTRACE(opic, ("interrupt %d - ignore withdrawn (active low)\n", src->nr)); else DTRACE(opic, ("interrupt %d - ignore high level (active low)\n", src->nr)); ASSERT(!src->is_multicast); src->pending = 0; } else if (!src->is_level_triggered && src->is_positive_polarity && !asserted) { DTRACE(opic, ("interrupt %d - ignore falling edge (positive edge trigered)\n", src->nr)); } else if (!src->is_level_triggered && !src->is_positive_polarity && asserted) { DTRACE(opic, ("interrupt %d - ignore rising edge (negative edge trigered)\n", src->nr)); } else if (src->in_service != 0) { /* leave the interrupt where it is */ ASSERT(!src->is_multicast); ASSERT(src->pending == 0 || src->pending == src->in_service); src->pending = src->in_service; DTRACE(opic, ("interrupt %ld - ignore already in service to 0x%lx\n", (long)src->nr, (long)src->in_service)); } else if (src->pending != 0) { DTRACE(opic, ("interrupt %ld - ignore still pending to 0x%lx\n", (long)src->nr, (long)src->pending)); } else { /* delivery is needed */ opic_interrupt_destination *dest = find_lowest_dest(me, opic, src); if (can_deliver(me, src, dest)) { dest->current_pending = src; src->pending = dest->bit; DTRACE(opic, ("interrupt %d - delivered to %d\n", src->nr, dest->nr)); assert_interrupt(me, opic, dest); } else { src->pending = src->destination; /* any can take this */ DTRACE(opic, ("interrupt %ld - pending to 0x%lx\n", (long)src->nr, (long)src->pending)); } }}static unsigneddo_interrupt_acknowledge_register_N_read(device *me, hw_opic_device *opic, int dest_nr){ opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr]; unsigned vector; ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations); ASSERT(dest_nr == dest->nr); /* try the current pending */ if (can_deliver(me, dest->current_pending, dest)) { ASSERT(dest->current_pending->pending & dest->bit); vector = deliver_pending(me, opic, dest); DTRACE(opic, ("interrupt ack %d - entering %d (pending) - vector %d (%d), priority %d\n", dest->nr, dest->current_in_service->nr, dest->current_in_service->vector, vector, dest->current_in_service->priority)); } else { /* try for something else */ dest->current_pending = find_interrupt_for_dest(me, opic, dest, pending_interrupt); if (can_deliver(me, dest->current_pending, dest)) { vector = deliver_pending(me, opic, dest); DTRACE(opic, ("interrupt ack %d - entering %d (not pending) - vector %d (%d), priority %d\n", dest->nr, dest->current_in_service->nr, dest->current_in_service->vector, vector, dest->current_in_service->priority)); } else { dest->current_pending = NULL; vector = opic->spurious_vector; DTRACE(opic, ("interrupt ack %d - spurious interrupt %d\n", dest->nr, vector)); } } return vector;}static voiddo_end_of_interrupt_register_N_write(device *me, hw_opic_device *opic, int dest_nr, unsigned reg){ opic_interrupt_destination *dest = &opic->interrupt_destination[dest_nr]; ASSERT(dest_nr >= 0 && dest_nr < opic->nr_interrupt_destinations); ASSERT(dest_nr == dest->nr); /* check the value written is zero */ if (reg != 0) { DTRACE(opic, ("eoi %d - ignoring nonzero value\n", dest->nr)); } /* user doing wierd things? */ if (dest->current_in_service == NULL) { DTRACE(opic, ("eoi %d - strange, no current interrupt\n", dest->nr)); return; } /* an internal stuff up? */ if (!(dest->current_in_service->in_service & dest->bit)) { device_error(me, "eoi %d - current interrupt not in service", dest->nr); } /* find what was probably the previous in service interrupt */ dest->current_in_service->in_service &= ~dest->bit; DTRACE(opic, ("eoi %d - ending %d - priority %d, vector %d\n", dest->nr, dest->current_in_service->nr, dest->current_in_service->priority, dest->current_in_service->vector)); dest->current_in_service = find_interrupt_for_dest(me, opic, dest, in_service_interrupt); if (dest->current_in_service != NULL) DTRACE(opic, ("eoi %d - resuming %d - priority %d, vector %d\n", dest->nr, dest->current_in_service->nr, dest->current_in_service->priority, dest->current_in_service->vector)); else DTRACE(opic, ("eoi %d - resuming none\n", dest->nr));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -