/* IBM_PROLOG_BEGIN_TAG */
/* This is an automatically generated prolog. */
/* */
/* $Source: src/ssx/occhw/occhw_pba.c $ */
/* */
/* OpenPOWER OnChipController Project */
/* */
/* Contributors Listed Below - COPYRIGHT 2015,2016 */
/* [+] 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 */
/// \file occhw_pba.c
/// \brief procedures for pba setup and operation.
#include "ssx.h"
#include "occhw_pba.h"
#include "occhw_scom.h"
#include "occhw_common.h"
#include "polling.h"
// Internal API to set up a PBA BAR
static int
pba_bar_set(int idx, uint64_t pb_base, uint8_t pb_scope)
{
int rc ;
pba_barn_t bar ;
bar.fields.cmd_scope = pb_scope ;
bar.fields.addr = pb_base >> PBA_LOG_SIZE_MIN ;
rc = putscom(PBA_BARN(idx), bar.value);
return rc ;
}
// Internal API to set up a PBA BAR Mask. The mask covers bits 23:43, address
// bits 44:63 are always allowed.
static int
pba_barmask_set(int idx, uint64_t mask)
{
int rc ;
pba_barmskn_t barmask ;
barmask.value = mask & 0x000001FFFFF00000ull;
rc = putscom(PBA_BARMSKN(idx), barmask.value);
return rc ;
}
/// Procedure to allocate a PowerBus address block using the bridge.
///
/// \param idx The BAR set to use, in the range 0..3.
///
/// \param base The 50-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 1MB (2**20), the maximum size is 2TB (2**41)
///
/// This is a validation/test procedure only, since setting up the PBA BARs is
/// normally reserved to pHyp and FSP. If the MMU is enabled and the PBA
/// mapping will be referenced by the 405, then an MMU mapping will also have
/// to be created (separately) for the block.
///
/// This procedure is not the complete setup for large memory areas. Memory
/// areas that require the extended address will have to set that up
/// separately.
///
/// \retval 0 Success
///
/// \retval PBA_INVALID_ARGUMENT_BARSET One or more of the parameter
/// restrictions were violated.
///
/// \retval PBA_SCOM_ERROR1 or PBA_SCOM_ERROR2 An attempt to write a PBA SCOM
/// register to set up the BARs produced a non-zero return code.
int
pba_barset_initialize(int idx, uint64_t base, int log_size)
{
uint64_t mask ;
mask = (0x1ull << log_size) - 1;
if (SSX_ERROR_CHECK_API)
{
SSX_ERROR_IF((idx < 0) ||
(idx > 3) ||
(log_size < PBA_LOG_SIZE_MIN) ||
(log_size > PBA_LOG_SIZE_MAX) ||
((base & mask) != 0),
PBA_INVALID_ARGUMENT_BARSET);
}
if (pba_bar_set(idx, base, PBA_POWERBUS_COMMAND_SCOPE_DEFAULT))
{
SSX_ERROR(PBA_SCOM_ERROR1);
}
if (pba_barmask_set(idx, mask))
{
SSX_ERROR(PBA_SCOM_ERROR2);
}
return 0 ;
}
// polling() function for PBA Slave reset
//
// Slave reset for PBA is a complex issue, especially in cases where the
// entity requesting the reset may be executing from main memory, i.e.
// continuing to read to or write from the slave being reset. To work around
// potential issues if the 405 is attempting to reset its own slave, the code
// that polls for reset is PowerBus cache-line aligned, and we re-hit the
// reset button each time we get an unsuccessful poll for the reset being
// done. This should guarantee that the slave will go to reset status as soon
// as any PowerBus blockages (if any) clear and the master stops either
// reading or writing the slave port. For details see HW228485.
int
_pba_slave_reset_poll(void* arg, int* done) ALIGNED_ATTRIBUTE(128);
int
_pba_slave_reset_poll(void* arg, int* done)
{
int id;
pba_slvrst_t psr;
id = (int)arg;
psr.value = 0;
psr.fields.set = PBA_SLVRST_SET(id);
out64(PBA_SLVRST, psr.value);
psr.value = in64(PBA_SLVRST);
*done = !(psr.fields.in_prog & PBA_SLVRST_IN_PROG(id));
return 0;
}
/// Reset a PBA slave with explicit timeout.
///
/// \param id A PBA slave id in the range 0..3
///
/// \param timeout A value of SsxInterval type. The special value
/// SSX_WAIT_FOREVER indicates no timeout.
///
/// \param sleep A value of SsxInterval type. Callers using the explicit
/// timeout form can request that the thread sleeps between polls; See
/// documentation for the polling() API.
///
/// This form of pba_slave_reset() gives the caller control over timeouts,
/// sleeping and error handling.
///
/// \retval 0 Success
///
/// \retval -PBA_INVALID_ARGUMENT_RESET The slave \a id parameter
/// is invalid.
///
/// \retval -PBA_SLVRST_TIMED_OUT1 The procedure timed out waiting for the PBA
/// to reset the slave.
int
_pba_slave_reset(int id, SsxInterval timeout, SsxInterval sleep)
{
int rc, closureRc;
if (SSX_ERROR_CHECK_API)
{
SSX_ERROR_IF((id < 0) || (id >= PBA_SLAVES),
PBA_INVALID_ARGUMENT_RESET);
}
rc = polling(&closureRc, _pba_slave_reset_poll, (void*)id, timeout, sleep);
if (rc == POLLING_TIMEOUT)
{
rc = -PBA_SLVRST_TIMED_OUT1;
}
return rc;
}
/// Reset a PBA slave with a default timeout
///
/// \param id A PBA slave id in the range 0..3
///
/// PBA slaves must be reset before being reprogrammed. Resetting a slave
/// also flushes any write buffers and invalidates any read buffers associated
/// with the slave. This procedure is for initialization/bringup/test only;
/// it has a non-deterministic run time due to the PowerBus so should not be
/// run as part of a hard real-time loop.
///
/// \retval 0 Success
///
/// \retval -SSX_INVALID_ARGUMENT_PBA_RESET The slave \a id parameter is
/// invalid.
///
/// \retval -PBA_SLVRST_TIMED_OUT2 The procedure timed out waiting for the PBA
/// to reset the slave.
int
pba_slave_reset(int id)
{
int rc;
rc = _pba_slave_reset(id, PBA_SLAVE_RESET_TIMEOUT, 0);
if (rc)
{
SSX_ERROR(PBA_SLVRST_TIMED_OUT2);
}
return rc;
}
/// Configure the PBAX mechanism
///
/// \param master If non-0, then this OCC will assume the role of the PBAX
/// master processor in the power domain. To avoid PBAX livelock there can
/// only be a single master in any power domain, and only the master is
/// allowed to issue PBAX broadcast transactions.
///
/// \param node The PBAX Node Id of this OCC
///
/// \param chip The PBAX Chip Id of this OCC
///
/// \param group_mask A bit mask indicating the broadcast group(s) (power
/// domain(s)) that this OCC belongs to.
///
/// This API sets up certain fields of the PBA_XCFG register according to the
/// parameters. Other fields in the register controlling hardware timeouts and
/// performance monitoring are unchanged.
///
/// \retval 0 Success
///
/// \retval -PBAX_INVALID_ARGUMENT_CONFIG One of the arguments is
/// not valid for some reason.
int
pbax_configure(int master, int group, int chip, int group_mask)
{
pba_xcfg_t pxc;
if (SSX_ERROR_CHECK_API)
{
SSX_ERROR_IF((group < 0) ||
(group >= PBAX_GROUPS) ||
(chip < 0) ||
(chip >= PBAX_CHIPS) ||
(group_mask < 0) ||
(group_mask > PBAX_GROUP_MASK_MAX),
PBAX_INVALID_ARGUMENT_CONFIG);
}
pxc.value = in64(PBA_XCFG);
pxc.fields.reservation_en = (master != 0);
pxc.fields.rcv_groupid = group;
pxc.fields.rcv_chipid = chip;
pxc.fields.rcv_brdcst_group = group_mask;
out64(PBA_XCFG, pxc.value);
return 0;
}
/// Create a PBAX abstract target
///
/// \param target An uninitialized or otherwise idle PbaxTarget object
///
/// \param type The transmission type, either PBAX_UNICAST or PBAX_BROADCAST
///
/// \param scope The PowerBus scope, either PBAX_GROUP or PBAX_SYSTEM
///
/// \param queue The receive queue index on the target, either 0 or 1
///
/// \param node The PBAX Node Id of the target
///
/// \param chip_or_group Either the PBAX Chip Id of the target (for unicast),
/// or the PBAX Group Id of the target (for broadcast)
///
/// Create an abstraction of a communication target for PBAX send operations,
/// for use with the _pbax_send() and _pbax_send() APIs. This API has no
/// knowledge of how the PBAX is configured, and therefore accepts all node,
/// chip and broadcast group ids as valid. However this API does assume that
/// all broadcast transactions are "real-time" and require a reservation.
///
/// \retval 0 Success
///
/// \retval -PBAX_INVALID_OBJECT The \a target parameter is NULL (0) or
/// otherwise invalid.
///
/// \retval -PBAX_INVALID_ARGUMENT_TARGET One or more of the arguments
/// is invalid.
int
pbax_target_create(PbaxTarget* target,
int type, int scope, int queue,
int group, int chip_or_group, int cnt)
{
if (SSX_ERROR_CHECK_API)
{
SSX_ERROR_IF(target == 0, PBAX_INVALID_OBJECT);
SSX_ERROR_IF(((type != PBAX_UNICAST) && (type != PBAX_BROADCAST)) ||
((scope != PBAX_GROUP) && (scope != PBAX_SYSTEM)) ||
((queue < 0) || (queue >= PBAX_QUEUES)),
PBAX_INVALID_ARGUMENT_TARGET);
}
target->target.value = 0;
target->target.fields.snd_scope = scope;
target->target.fields.snd_qid = queue;
target->target.fields.snd_type = type;
target->target.fields.snd_reservation = (type == PBAX_BROADCAST);
target->target.fields.snd_groupid = group;
target->target.fields.snd_chipid = chip_or_group;
target->target.fields.snd_cnt = cnt;
target->target.fields.vg_targe = 0xffff;
return 0;
}
/// Use PBAX to send 64 bits to a target with a caller-specified timeout
///
/// \param target An abstract PBAX target object
///
/// \param data The data to send
///
/// \param timeout The caller's timeout represented as an
/// SsxInterval. Use SSX_WAIT_FOREVER to indicate the caller is willing to
/// wait forever. A \a timeout of 0 indicates that the caller is not
/// willing to wait at all, and the call will either succeed immediately or
/// immediately return -PBAX_TIMEOUT.
///
/// The PBAX mechanism has a single outgoing channel that must be shared by
/// all processes that need to send messages using PBAX. Since messages sent
/// over PBAX may require an unknown amount of time to complete (due to
/// PowerBus traffic and blockages), this driver implements an agressive
/// policy that allows a sender to initiate a send transaction without waiting
/// for the final completion status. However this opens a window where a
/// subsequent writer may find the PBAX send mechanism already in use. Here
/// the new sender is obligated to busy-wait until the send mechanism is free,
/// as the PBAX send does not include an interrupt notification.
///
/// This form of the PBAX send operation accepts an explicit \a timeout value
/// as previously described. The timeout represents the amount of time that
/// the caller is willing to wait for a busy PBAX send mechanism to become
/// free. The PBAX hardware also includes mechanisms to time out the hardware
/// and modeably interrupt OCC in the event of lack of progress.
///
/// This API does not operate in a critical section or enforce any
/// synchronization protocol. Synchronization and software timeout management
/// in the case of multiple senders is the responsibility of the
/// application.
///
/// Unlike most other driver APIs this API can not be configured to "panic",
/// but instead always terminates with a 0 return code or an error status.
///
/// \retval 0 Success. Success only means that a transaction was sucessfully
/// initiated on an idle PBAX send machine.
///
/// \retval -PBAX_TIMEOUT The caller-specified timeout expired before the PBAX
/// send machine became free, but the PBAX send machine does not show error
/// status.
///
/// \retval -PBAX_SEND_ERROR The PBAXSNDSTAT.snd_error bit is asserted. It
/// is expected that this error will cause the PBA error interrupt to fire -
/// FFDC is collected by the interrupt handler.
int
_pbax_send(PbaxTarget* target, uint64_t data, SsxInterval timeout)
{
pba_xsndstat_t pss;
SsxTimebase start;
int rc, timed_out;
// The PBAX is always polled at least twice to guarantee that we always
// poll once after a timeout - unless the caller explicitly requested a 0
// timeout.
start = 0;
timed_out = 0;
do
{
pss.words.high_order = in32(PBA_XSNDSTAT);
if (pss.fields.snd_error)
{
rc = -PBAX_SEND_ERROR;
break;
}
if (!pss.fields.snd_in_progress)
{
rc = 0;
break;
}
if (start == 0)
{
start = ssx_timebase_get();
}
if ((timeout == 0) || timed_out)
{
rc = -PBAX_SEND_TIMEOUT;
break;
}
timed_out =
((timeout != SSX_WAIT_FOREVER) &&
((ssx_timebase_get() - start) > timeout));
}
while (1);
// Configure the send engine and initiate the write, which is kicked off
// by writing the high-order word of the send data register.
if (!rc)
{
out64(PBA_XSNDTX, target->target.value);
out32(PBA_XSNDDAT + 4, data >> 32);
out32(PBA_XSNDDAT, data & 0xffffffff);
}
return rc;
}
/// Use PBAX to send 64 bits to a target with a default timeout
///
/// \param target An abstract PBAX target object
///
/// \param data The data to send
///
/// This form of the PBAX send operation uses a default timeout, and may be
/// configured to panic in the event of timeouts or send errors. See
/// _pbax_send() for the specification of the underlying API.
///
/// This API does not operate in a critical section or enforce any
/// synchronization protocol. Synchronization and software timeout management
/// in the case of multiple senders is the responsibility of the
/// application.
///
/// \retval 0 Success. Success only means that a transaction was sucessfully
/// initiated on an idle PBAX send machine.
///
/// \retval -PBAX_TIMEOUT The caller-specified timeout expired before the PBAX
/// send machine became free.
///
/// \retval -PBAX_SEND_ERROR The PBAXSNDSTAT.snd_error bit is asserted.
int
pbax_send(PbaxTarget* target, uint64_t data)
{
int rc;
rc = _pbax_send(target, data, PBAX_SEND_DEFAULT_TIMEOUT);
if (rc)
{
if (rc == -PBAX_SEND_TIMEOUT)
{
SSX_ERROR(PBAX_SEND_TIMEOUT);
}
else
{
SSX_ERROR(PBAX_SEND_ERROR);
}
}
return rc;
}