diff options
Diffstat (limited to 'src/ssx/occhw/occhw_ocb.c')
-rw-r--r-- | src/ssx/occhw/occhw_ocb.c | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/src/ssx/occhw/occhw_ocb.c b/src/ssx/occhw/occhw_ocb.c new file mode 100644 index 0000000..237c333 --- /dev/null +++ b/src/ssx/occhw/occhw_ocb.c @@ -0,0 +1,359 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/ssx/occhw/occhw_ocb.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file occhw_ocb.c +/// \brief OCB-related drivers for OCCHW + +#include "ssx.h" + +//use compile-time default in case the OCB timer is never used -- grm +unsigned int g_ocb_timer_divider = OCB_TIMER_DIVIDER_DEFAULT; + +/// Reset an OCB timer +/// +/// \param timer A valid OCB timer index +/// +/// \param auto_reload A non-0 value indicates to run the timer in auto-reload +/// mode. +/// +/// \param timeout_ns The timeout specified in nanoseconds. The actual timeout +/// will be rounded down to the underlying timer tick, with a minimum 1 tick +/// timeout. However if the \a timeout_ns argument is explicity 0, then the +/// timer wil be initialized with a 0 timeout, effectively disabling the +/// timer. +/// +/// Reseting an OCB timer means rewriting the timer control register with a +/// potentially new auto-reload enable and new timeout value. This API also +/// clears any pending timer interrupt status. +/// +/// \retval 0 Success +/// +/// \retval -OCB_INVALID_ARGUMENT_TIMER Causes include illegal timer +/// numbers and illegal or unrepresntable timeouts. + +// Note that OCB_TIMER_FREQUENCY_HZ is in the range of 1-2 MHz. + +int +ocb_timer_reset(int timer, + int auto_reload, + int timeout_ns) +{ + ocb_otrn_t otr; + int ticks; + + //printk("ocb_timer_reset(%d, %d, %d)\n", + // timer, auto_reload, timeout_ns); + + if (timeout_ns != 0) { + ticks = MAX(1, timeout_ns / (1000000000 / OCB_TIMER_FREQUENCY_HZ)); + } else { + ticks = 0; + } + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((timer < 0) || + (timer >= OCB_TIMERS) || + (timeout_ns < 0) || + (ticks != ((uint16_t)ticks)), + OCB_INVALID_ARGUMENT_TIMER); + } + + otr.value = 0; + + otr.fields.timeout = 1; + otr.fields.control = 1; + otr.fields.auto_reload = (auto_reload != 0); + otr.fields.timer = ticks; + + out32(OCB_OTRN(timer), otr.value); + + return 0; +} + + +/// Set up an OCB timer and interrupt handler +/// +/// \param timer A valid OCB timer index +/// +/// \param auto_reload A non-0 value indicates to run the timer in auto-reload +/// mode. +/// +/// \param timeout_ns The timeout specified in nanoseconds. The actual timeout +/// will rounded down to the underlying timer tick, with a minimum 1 tick +/// timeout. However if the \a timeout_ns argument is explicity 0, then the +/// timer wil be initialized with a 0 timeout, effectively disabling the +/// timer. +/// +/// \param handler The interrupt handler for the timer interrupt +/// +/// \param arg The private argument of the interrupt handler +/// +/// \param priority The SSX/PPC405 interrupt priority to assign to the +/// interrupt. +/// +/// This API sets up and starts the timer and unmasks the timer +/// interrupt. Once set up, the timer can be reset using ocb_timer_reset(). As +/// a fine point of the specification, if a timer interrupt is already pending +/// when this API is invoked then that interrupt will be cleared. Only the +/// next interrupt (corresponding to the new programming) will be serviced by +/// the newly installed handler. +/// +/// Note that the interrupt handler is responsible for clearing the timer +/// interrupt status. An API ocb_timer_status_clear() is available for this. +/// +/// \retval 0 Success +/// +/// \retval -OCB_INVALID_ARGUMENT_TIMER Causes include illegal timer +/// numbers and illegal or unrepresntable timeouts. +/// +/// Other errors may be returned by the underlying calls to SSX routines that +/// set up interrupts. + +int +ocb_timer_setup(int timer, + int auto_reload, + int timeout_ns, + SsxIrqHandler handler, + void *arg, + int priority) +{ + int rc; + tpc_hpr2_t l_hpr2; + + do + { + //Read Hang Pulse Register 2 to get the log base 2 of the ocb clock divider -- grm + rc = getscom(TPC_HPR2, &l_hpr2.value); + if(rc) + { + break; + } + + g_ocb_timer_divider = 1 << l_hpr2.fields.hang_pulse_reg; + + //printk("ocb_timer_setup(%d, %d, %d, %p, %p, %d)\n", + // timer, auto_reload, timeout_ns, + // handler, arg, priority); + + ssx_irq_disable(OCCHW_IRQ_OCC_TIMER0 + timer); + + ssx_irq_setup(OCCHW_IRQ_OCC_TIMER0 + timer, + SSX_IRQ_POLARITY_ACTIVE_HIGH, + SSX_IRQ_TRIGGER_LEVEL_SENSITIVE); + + ssx_irq_handler_set(OCCHW_IRQ_OCC_TIMER0 + timer, + handler, + arg, + priority); + + rc = ocb_timer_reset(timer, auto_reload, timeout_ns); + + ssx_irq_enable(OCCHW_IRQ_OCC_TIMER0 + timer); + }while(0); + + return rc; +} + + +/// Generate an core interrupt via the PSI Host Bridge +/// +/// Setting OCB_OCCMISC.core_ext_int to 1 causes a wire to pulse to the PSI +/// Host Bridge to allow the presentation of an external interrupt to a core +/// thread. The core thread to be interrupted is controlled by the XIVR - OCC +/// register, SCOM 02010916. Normally the hypervisor will set up the PSI Host +/// Bridge. This procedure allows OCC to send an interrupt to the hypervisor. +/// +/// \retval 0 Success + +// The interrupt is generated by causing a 0->1 transation on +// OCB_OCCMISC.core_ext_intr. This is implemented here using the WAND/WOR +// forms of the register. + +int +ocb_core_interrupt() +{ + ocb_occmisc_t oo; + + oo.value = 0; + oo.fields.core_ext_intr = 1; + out32(OCB_OCCMISC_CLR, oo.value); + out32(OCB_OCCMISC_OR, oo.value); + + return 0; +} + + +/// Procedure to setup a linear window on an indirect channel +/// +/// +/// Since the linear window access is restricted to the SRAM region of +/// OCI space, Bits 0:4 of the base parameter are don't care and will be +/// overwritten with b'11000' +/// +/// +/// \todo double check SRAM region restriction +/// +/// \param channel The indirect channel to use, in the range 0..2. +/// +/// \param base The 32-bit PowerBus base address where the block starts. This +/// address must be aligned to the \a log_size. +/// +/// \param log_size The base 2 logarithm of the block size, in bytes. The +/// minimum size is 8B (2**3), the maximum size is 32KB (2**15) +/// +/// +/// \retval 0 Success +/// +/// \retval OCB_INVALID_ARGUMENT_LW_INIT One or more of the parameter +/// restrictions were violated. +/// +/// \retval OCB_SCOM_ERROR An attempt to write a PBA SCOM register to set up +/// the BARs produced a non-zero return code. + +int +ocb_linear_window_initialize(int channel, uint32_t base, int log_size) +{ + uint32_t mask ; + ocb_ocblwcrn_t ocblwcrn; + ocb_ocblwsbrn_t ocblwsbrn; + + // create mask for checking + mask = (0x1ull << log_size) - 1; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((channel < 0) || + (channel > 3) || + (log_size < OCB_LW_LOG_SIZE_MIN) || + (log_size > OCB_LW_LOG_SIZE_MAX) || + ((base & mask) != 0), + OCB_INVALID_ARGUMENT_LW_INIT); + } + + // now invert mask for use in ocb linear window setup + mask = ~mask; + + + // Enable LW mode + ocblwcrn.fields.linear_window_enable = 1; + // Select bits(12:28) of OCI addr for the LW bar + ocblwcrn.fields.linear_window_bar = (base >> 3) & 0x1FFFF; + ocblwcrn.fields.linear_window_mask = (mask >> 3) & 0xFFF; + out32(OCB_OCBLWCRN(channel), ocblwcrn.value); + + // Configure LW region for SRAM access + ocblwsbrn.fields.linear_window_region = 7; + // Select bits(5:11) of OCI addr for the LW base + ocblwsbrn.fields.linear_window_base = (base >> 20) & 0x7F; + out32(OCB_OCBLWSBRN(channel), ocblwsbrn.value); + + return 0 ; +} + +/// Procedure to disable a linear window on an indirect channel +/// +/// This procedure will disable the linear window while maintaining +/// the linear_window_bar and linear_window_mask settings +/// +/// \param channel The indirect channel to disable, in the range 0..2. +/// +/// \retval 0 Success +/// +/// \retval OCB_INVALID_ARGUMENT_LW_DISABLE One or more of the parameter +/// restrictions were violated. +/// + +int +ocb_linear_window_disable(int channel) +{ + ocb_ocblwcrn_t ocblwcrn; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((channel < 0) || + (channel > 3), + OCB_INVALID_ARGUMENT_LW_DISABLE); + } + + ocblwcrn.value = in32(OCB_OCBLWCRN(channel)); + // Disable LW mode + ocblwcrn.fields.linear_window_enable = 0; + out32(OCB_OCBLWCRN(channel), ocblwcrn.value); + + return 0 ; +} + + +/// Procedure for setting up untrusted mode for an indirect channel +/// +/// Note that the OCC FW is expected to enable the channel for FSP +/// access. As an untrusted master, the FSP cannot configure this +/// in a chip running in trusted mode. The SBE is considered a trusted +/// master. +/// +/// +/// \param channel The indirect channel to use, in the range 0..2 +/// Note that this bit is not used for indirect channel 3. +/// +/// +/// \param allow_untrusted Enable untrusted PIB masters +/// access to the indirect channel being configured. If allow_untrusted is +/// not enabled and the chip is running in trusted mode, then any untrusted +/// PIB master will get an offline return code when attempting to write +/// the indirect channel. 0 = Disable, 1 = Enable +/// +/// \retval 0 Success +/// +/// \retval OCB_INVALID_ARGUMENT_UNTRUST One or more of the parameter +/// restrictions were violated. +/// + + +//NOTE: The OCBICR register seems to have gone away in P9 and we didn't ever +// call this function in P8 so I'm removing this function for now. (grm) +#if 0 +int +ocb_allow_untrusted_initialize(int channel, int allow_untrusted) +{ + ocb_ocbicrn_t ocbicrn; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((channel < 0) || + (channel > 2) || + (allow_untrusted < 0) || + (allow_untrusted > 1), + OCB_INVALID_ARGUMENT_UNTRUST); + } + + // Configure allow_unsecure_pib_masters bit + ocbicrn.fields.allow_unsecure_pib_masters = allow_untrusted; + out32(OCB_OCBICRN(channel), ocbicrn.value); + + return 0 ; +} +#endif |