void *ppc_exc_vector_address(unsigned vector) { uintptr_t vector_base = 0xfff00000; uintptr_t vector_offset = vector << 8; if (ppc_cpu_has_altivec()) { if (vector == ASM_60X_VEC_VECTOR) { vector_offset = ASM_60X_VEC_VECTOR_OFFSET; } } if (ppc_cpu_is(PPC_405)) { switch (vector) { case ASM_BOOKE_FIT_VECTOR: vector_offset = ASM_PPC405_FIT_VECTOR_OFFSET; break; case ASM_BOOKE_WDOG_VECTOR: vector_offset = ASM_PPC405_WDOG_VECTOR_OFFSET; break; case ASM_TRACE_VECTOR: vector_offset = ASM_PPC405_TRACE_VECTOR_OFFSET; break; case ASM_PPC405_APU_UNAVAIL_VECTOR: vector_offset = ASM_60X_VEC_VECTOR_OFFSET; default: break; } } if ( ppc_cpu_is_bookE() == PPC_BOOKE_STD || ppc_cpu_is_bookE() == PPC_BOOKE_E500 ) { if (vector < sizeof(ivor_values) / sizeof(ivor_values [0])) { vector_offset = ((uintptr_t) ivor_values [vector]) << 4; } else { vector_offset = 0; } } if (bsp_exceptions_in_RAM) { vector_base = ppc_exc_vector_base; } return (void *) (vector_base + vector_offset); }
/* legacy mode for bookE DEC exception; * to avoid the double layer of function calls * (dec_handler_bookE -> C_dispatch_irq_handler -> user handler) * it is preferrable for the user to hook the DEC * exception directly. * However, the legacy mode works with less modifications * of user code. */ int C_dispatch_dec_handler_bookE (BSP_Exception_frame *frame, unsigned int excNum) { /* clear interrupt; we must do this * before C_dispatch_irq_handler() * re-enables MSR_EE. * Note that PPC405 uses a different SPR# for TSR */ if ( ppc_cpu_is_bookE()==PPC_BOOKE_405) _write_PPC405_TSR( BOOKE_TSR_DIS ); else _write_BOOKE_TSR( BOOKE_TSR_DIS ); return C_dispatch_irq_handler(frame, ASM_DEC_VECTOR); }
static uint32_t ppc_exc_get_DAR_dflt(void) { if (ppc_cpu_is_60x()) return PPC_SPECIAL_PURPOSE_REGISTER(PPC_DAR); else switch (ppc_cpu_is_bookE()) { default: break; case PPC_BOOKE_STD: case PPC_BOOKE_E500: return PPC_SPECIAL_PURPOSE_REGISTER(DEAR_BOOKE); case PPC_BOOKE_405: return PPC_SPECIAL_PURPOSE_REGISTER(DEAR_405); } return 0xdeadbeef; }
int BSP_rtems_irq_mngt_set(rtems_irq_global_settings* config) { int i; rtems_interrupt_level level; rtems_irq_connect_data* vchain; /* * Store various code accelerators */ internal_config = config; default_rtems_entry = config->defaultEntry; rtems_hdl_tbl = config->irqHdlTbl; rtems_interrupt_disable(level); if ( !BSP_setup_the_pic(config) ) { printk("PIC setup failed; leaving IRQs OFF\n"); return 0; } for ( i = config->irqBase; i < config->irqBase + config->irqNb; i++ ) { for( vchain = &rtems_hdl_tbl[i]; ((int)vchain != -1 && vchain->hdl != default_rtems_entry.hdl); vchain = (rtems_irq_connect_data*)vchain->next_handler ) { if (vchain->on) vchain->on(vchain); } if ( vchain != &rtems_hdl_tbl[i] ) { /* at least one handler registered */ BSP_enable_irq_at_pic(i); } else { /* Do NOT disable; there might be boards with cascaded * interrupt controllers where the BSP (incorrectly) does * not ignore the cascaded interrupts in BSP_disable_irq_at_pic()! * Instead, we rely on BSP_setup_the_pic() for a good * initial configuration. * BSP_disable_irq_at_pic(i); */ } } rtems_interrupt_enable(level); { ppc_exc_set_handler(ASM_EXT_VECTOR, C_dispatch_irq_handler); if ( ppc_cpu_is_bookE() ) { /* bookE decrementer interrupt needs to be cleared BEFORE * dispatching the user ISR (because the user ISR is called * with EE enabled) * We do this so that existing DEC handlers can be used * with minor modifications. */ ppc_exc_set_handler(ASM_BOOKE_DEC_VECTOR, C_dispatch_dec_handler_bookE); } else { ppc_exc_set_handler(ASM_DEC_VECTOR, C_dispatch_irq_handler); } } return 1; }
rtems_device_driver Clock_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { /* Current CPU type */ ppc_cpu_id_t cpu_type = get_ppc_cpu_type(); /* Make major/minor available to others such as shared memory driver */ rtems_clock_major = major; rtems_clock_minor = minor; /* * Set default ticker. * * The function rtems_clock_tick() returns a status code. This value * will be discarded since the RTEMS documentation claims that it is * always successful. */ ppc_clock_tick = (void (*)(void)) rtems_clock_tick; /* Set the decrementer to the maximum value */ ppc_set_decrementer_register( PPC_CLOCK_DECREMENTER_MAX); /* Decrementer value */ ppc_clock_decrementer_value = bsp_clicks_per_usec * rtems_configuration_get_microseconds_per_tick() - 1; /* Check decrementer value */ if (ppc_clock_decrementer_value == 0) { ppc_clock_decrementer_value = PPC_CLOCK_DECREMENTER_MAX; RTEMS_SYSLOG_ERROR( "decrementer value would be zero, will be set to maximum value instead\n"); } /* Set the nanoseconds since last tick handler */ rtems_clock_set_nanoseconds_extension( ppc_clock_nanoseconds_since_last_tick); if (ppc_cpu_is_bookE()) { /* Set decrementer auto-reload value */ PPC_SET_SPECIAL_PURPOSE_REGISTER( BOOKE_DECAR, ppc_clock_decrementer_value); /* Install exception handler */ ppc_exc_set_handler( ASM_BOOKE_DEC_VECTOR, ppc_clock_exception_handler_booke); /* Enable decrementer and auto-reload */ PPC_SET_SPECIAL_PURPOSE_REGISTER_BITS( BOOKE_TCR, BOOKE_TCR_DIE | BOOKE_TCR_ARE); } else if (cpu_type == PPC_e300c2 || cpu_type == PPC_e300c3) { /* TODO: Not tested for e300c2 */ /* Enable auto-reload */ PPC_SET_SPECIAL_PURPOSE_REGISTER_BITS( HID0, 0x00000040); /* Install exception handler */ ppc_exc_set_handler( ASM_DEC_VECTOR, ppc_clock_exception_handler_e300); } else { /* Here the decrementer value is actually the interval */ ++ppc_clock_decrementer_value; /* Initialize next time base */ ppc_clock_next_time_base = ppc_time_base() + ppc_clock_decrementer_value; /* Install exception handler */ ppc_exc_set_handler( ASM_DEC_VECTOR, ppc_clock_exception_handler_first); } /* Set the decrementer value */ ppc_set_decrementer_register( ppc_clock_decrementer_value); return RTEMS_SUCCESSFUL; }
rtems_status_code ppc_exc_make_prologue( unsigned vector, ppc_exc_category category, uint32_t *prologue, size_t *prologue_size ) { const uint32_t *prologue_template = NULL; size_t prologue_template_size = 0; bool fixup_vector = false; if (!ppc_exc_is_valid_category(category)) { return RTEMS_INVALID_NUMBER; } if ( ppc_cpu_has_shadowed_gprs() && (vector == ASM_60X_IMISS_VECTOR || vector == ASM_60X_DLMISS_VECTOR || vector == ASM_60X_DSMISS_VECTOR) ) { prologue_template = ppc_exc_tgpr_clr_prolog; prologue_template_size = (size_t) ppc_exc_tgpr_clr_prolog_size; } else if ( category == PPC_EXC_CLASSIC && ppc_cpu_is_bookE() != PPC_BOOKE_STD && ppc_cpu_is_bookE() != PPC_BOOKE_E500 ) { prologue_template = ppc_exc_min_prolog_auto; prologue_template_size = (size_t) ppc_exc_min_prolog_size; } else if ( category == PPC_EXC_CLASSIC_ASYNC && ppc_cpu_is_bookE() == PPC_BOOKE_E500 && (ppc_interrupt_get_disable_mask() & MSR_CE) == 0 ) { prologue_template = ppc_exc_min_prolog_async_tmpl_normal; prologue_template_size = (size_t) ppc_exc_min_prolog_size; fixup_vector = true; } else { prologue_template = ppc_exc_prologue_templates [category]; prologue_template_size = (size_t) ppc_exc_min_prolog_size; fixup_vector = true; } if (prologue_template_size <= *prologue_size) { *prologue_size = prologue_template_size; memcpy(prologue, prologue_template, prologue_template_size); if (!ppc_exc_create_branch_op(vector, prologue, prologue_template_size)) { return RTEMS_INVALID_ADDRESS; } if (fixup_vector) { if (vector <= 0x7fffU) { prologue [PPC_EXC_PROLOG_VEC_OFFSET] = (prologue [PPC_EXC_PROLOG_VEC_OFFSET] & 0xffff8000U) | (vector & 0x7fffU); } else { return RTEMS_INVALID_ID; } } } else { return RTEMS_INVALID_SIZE; } return RTEMS_SUCCESSFUL; }
void ppc_exc_initialize_with_vector_base( uintptr_t interrupt_stack_begin, uintptr_t interrupt_stack_size, void *vector_base ) { rtems_status_code sc = RTEMS_SUCCESSFUL; const ppc_exc_categories *const categories = ppc_exc_current_categories(); unsigned vector = 0; uint32_t sda_base = 0; uint32_t r13 = 0; if (categories == NULL) { ppc_exc_fatal_error(); } /* Assembly code needs SDA_BASE in r13 (SVR4 or EABI). Make sure * early init code put it there. */ __asm__ volatile ( "lis %0, _SDA_BASE_@h\n" "ori %0, %0, _SDA_BASE_@l\n" "mr %1, 13\n" : "=r" (sda_base), "=r"(r13) ); if (sda_base != r13) { ppc_exc_fatal_error(); } ppc_exc_initialize_interrupt_stack(interrupt_stack_begin, interrupt_stack_size); #ifndef PPC_EXC_CONFIG_BOOKE_ONLY /* Use current MMU / RI settings when running C exception handlers */ ppc_exc_msr_bits = ppc_machine_state_register() & (MSR_DR | MSR_IR | MSR_RI); #ifdef __ALTIVEC__ /* Need vector unit enabled to save/restore altivec context */ ppc_exc_msr_bits |= MSR_VE; #endif #endif /* PPC_EXC_CONFIG_BOOKE_ONLY */ if (ppc_cpu_is_bookE() == PPC_BOOKE_STD || ppc_cpu_is_bookE() == PPC_BOOKE_E500) { ppc_exc_initialize_booke(vector_base); } for (vector = 0; vector <= LAST_VALID_EXC; ++vector) { ppc_exc_category category = ppc_exc_category_for_vector(categories, vector); if (category != PPC_EXC_INVALID) { void *const vector_address = ppc_exc_vector_address(vector, vector_base); uint32_t prologue [16]; size_t prologue_size = sizeof(prologue); sc = ppc_exc_make_prologue( vector, vector_base, category, prologue, &prologue_size ); if (sc != RTEMS_SUCCESSFUL) { ppc_exc_fatal_error(); } ppc_code_copy(vector_address, prologue, prologue_size); } } #ifndef PPC_EXC_CONFIG_BOOKE_ONLY /* If we are on a classic PPC with MSR_DR enabled then * assert that the mapping for at least this task's * stack is write-back-caching enabled (see README/CAVEATS) * Do this only if the cache is physically enabled. * Since it is not easy to figure that out in a * generic way we need help from the BSP: BSPs * which run entirely w/o the cache may set * ppc_exc_cache_wb_check to zero prior to calling * this routine. * * We run this check only after exception handling is * initialized so that we have some chance to get * information printed if it fails. * * Note that it is unsafe to ignore this issue; if * the check fails, do NOT disable it unless caches * are always physically disabled. */ if (ppc_exc_cache_wb_check && (MSR_DR & ppc_exc_msr_bits)) { /* The size of 63 assumes cache lines are at most 32 bytes */ uint8_t dummy[63]; uintptr_t p = (uintptr_t) dummy; /* If the dcbz instruction raises an alignment exception * then the stack is mapped as write-thru or caching-disabled. * The low-level code is not capable of dealing with this * ATM. */ p = (p + 31U) & ~31U; __asm__ volatile ("dcbz 0, %0"::"b" (p)); /* If we make it thru here then things seem to be OK */ }
rtems_device_driver Clock_initialize( rtems_device_major_number major, rtems_device_minor_number minor, void *arg ) { uint64_t frequency = bsp_time_base_frequency; uint64_t us_per_tick = rtems_configuration_get_microseconds_per_tick(); uint32_t interval = (uint32_t) ((frequency * us_per_tick) / 1000000); /* * Set default ticker. */ ppc_clock_tick = rtems_timecounter_tick; if (ppc_cpu_is_bookE() != PPC_BOOKE_405) { /* Decrementer value */ ppc_clock_decrementer_value = interval - 1; /* Check decrementer value */ if (ppc_clock_decrementer_value == 0) { ppc_clock_decrementer_value = PPC_CLOCK_DECREMENTER_MAX; RTEMS_SYSLOG_ERROR( "decrementer value would be zero, will be set to maximum value instead\n"); } if (ppc_cpu_is_bookE()) { /* Set decrementer auto-reload value */ PPC_SET_SPECIAL_PURPOSE_REGISTER( BOOKE_DECAR, ppc_clock_decrementer_value); /* Install exception handler */ ppc_exc_set_handler( ASM_BOOKE_DEC_VECTOR, ppc_clock_exception_handler_booke); /* Enable decrementer and auto-reload */ PPC_SET_SPECIAL_PURPOSE_REGISTER_BITS( BOOKE_TCR, BOOKE_TCR_DIE | BOOKE_TCR_ARE); } else { /* Here the decrementer value is actually the interval */ ++ppc_clock_decrementer_value; /* Initialize next time base */ ppc_clock_next_time_base = ppc_time_base() + ppc_clock_decrementer_value; /* Install exception handler */ ppc_exc_set_handler( ASM_DEC_VECTOR, ppc_clock_exception_handler_first); } /* Set the decrementer value */ ppc_set_decrementer_register( ppc_clock_decrementer_value); } else { /* PIT interval value */ ppc_clock_decrementer_value = interval; /* Install exception handler */ ppc_exc_set_handler(ASM_BOOKE_DEC_VECTOR, ppc_clock_exception_handler_ppc405); /* Enable PIT and auto-reload */ PPC_SET_SPECIAL_PURPOSE_REGISTER_BITS(PPC405_TCR, BOOKE_TCR_DIE | BOOKE_TCR_ARE); /* Set PIT auto-reload and initial value */ PPC_SET_SPECIAL_PURPOSE_REGISTER(PPC405_PIT, interval); } /* Install timecounter */ ppc_tc.tc_get_timecount = ppc_get_timecount; ppc_tc.tc_counter_mask = 0xffffffff; ppc_tc.tc_frequency = frequency; ppc_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER; rtems_timecounter_install(&ppc_tc); return RTEMS_SUCCESSFUL; }