/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: import/chips/p9/common/pmlib/occlib/ipc_api.h $               */
/*                                                                        */
/* OpenPOWER HCODE Project                                                */
/*                                                                        */
/* COPYRIGHT 2015,2017                                                    */
/* [+] 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                                                     */
#ifndef __IPC_API_H__
#define __IPC_API_H__

/// \file ipc_api.h
/// \brief Common header for Interprocessor Communications API
///
#include "ipc_structs.h"

#ifndef __ASSEMBLER__
///////////////////////////////////////////////////////////////////////////////
/// Initialize an IPC command message
///
/// \param msg A pointer to the command message
///
/// \param func_id A user-defined function ID that is known to both the sender
/// and receiver of a command message.  (Defined in \a ipc_func_ids.h)
///
/// \param resp_callback A user-defined function that should be called when
/// the message is returned as a response to the command.  This must be set
/// to 0 if no callback function should be called.
///
/// \param callback_arg A pointer to user-defined data that will be passed in
/// to the callback function when it is called. This should be set to 0 if
/// no data needs to be passed.
///
/// This function (or \a ipc_init_msgq_msg) must be called on a message at
/// least once before it is sent via the \a ipc_send_msg interface.
///
/// There are two types of function ID's. Function ID's that only work on a
/// single processor are called \e single-target ID's.  These function
/// ID's have the target ID embedded as part of the ID.  FUnction ID's
/// that are supported on multiple processors are called \e multi-target ID's.
/// Command messages associated with multi-target function ID's must go through
/// the extra step of setting the target ID by calling the \a ipc_set_cmd_target
/// interface on the command message.
///
/// If a callback function is provided, that callback function should cause
/// (directly or indirectly) the \ipc_free_msg interface to be called once it
/// is known that it is safe for the message to be reused (sent as a command
/// again).
///
void ipc_init_msg(ipc_msg_t* msg,
                  uint32_t func_id,
                  ipc_msg_handler_t resp_callback,
                  void* callback_arg);

//Use these to statically initialize an IPC message
#define IPC_MSG_INIT(_func_id, _resp_callback, _callback_arg) \
    {\
        {.node = KERN_DEQUE_ELEMENT_INIT()}, \
        .func_id.word32 = _func_id, \
                          .ipc_rc = IPC_RC_SUCCESS, \
                                    .resp_callback = _resp_callback, \
                                            .callback_arg = _callback_arg \
    }

#define IPC_MSG_CREATE(msg_name, _func_id, _resp_callback, _callback_arg) \
    ipc_msg_t msg_name = IPC_MSG_INIT(_func_id, _resp_callback, _callback_arg)

///////////////////////////////////////////////////////////////////////////////
/// Free up a message to be reused.
///
/// \param msg a pointer to a message.
///
/// This interface should be called on a message when it is known that it is
/// safe to reuse the message.  Normally, this would be one of the last things
/// performed in the response callback function for a command, but it may also
/// be called when it is known that a peer has stopped functioning.
///
static inline void ipc_free_msg(ipc_msg_t* msg)
{
    msg->func_id.active_flag = 0;
}

///////////////////////////////////////////////////////////////////////////////
/// Set the target ID for a multi-target command message.
///
/// \param cmd A pointer to an initialized command message.
///
/// \param target_id The target ID of the processor the command is intended
/// for.
///
/// The following return codes are possible:
///
/// \retval IPC_RC_SUCCESS The command's target ID was updated.
///
/// \retval IPC_RC_INVALID_FUNC_ID The function ID associated with this
/// command is not a valid mult-target function ID.
///
int ipc_set_cmd_target(ipc_msg_t* cmd, uint32_t target_id);


///////////////////////////////////////////////////////////////////////////////
/// Send a message as a command
///
/// \param cmd A pointer to an initialized command message
///
/// It is expected that at some point prior to calling this function the
/// message was initialized with a call to \a ipc_init_msg or
/// \a ipc_init_msgq_msg.
///
/// Once a message has been sent it is not safe to send again until it has been
/// sent back to the sender as a response.
///
/// The following return codes are possible:
///
/// \retval IPC_RC_SUCCESS The message was successfully placed on the target's
/// receive buffer.
///
/// \retval IPC_RC_SELF_BLOCKED The call was made prior to calling
/// \a ipc_enable.
///
/// \retval IPC_RC_INVALID_FUNC_ID The command was initialized with an invalid
/// function ID.
///
/// \retval IPC_RC_MSG_ACTIVE The message is currently in use.
///
/// \retval IPC_RC_INVALID_TARGET_ID The message was initialized with an
/// invalid target ID.  This can happen if a multi-target command has not had
/// its target set via the \a ipc_set_cmd_target function at least one time.
///
/// \retval IPC_RC_BUFFER_FULL The command could not be sent because the target's
/// receive buffer is full.
///
/// \retval IPC_RC_TARGET_BLOCKED The command could not be sent because the
/// target is blocking any new messages.
///
int ipc_send_cmd(ipc_msg_t* cmd);


////////////////////////////////////////////////////////////////////////////////
/// Send a command message back to the sender as a response message with status.
///
/// \param rsp A pointer to a message that was recieved as a command message.
///
/// \param ipc_rc This should be either \a IPC_RC_SUCCESS if the command was
/// successful or \a IPC_RC_CMD_FAILED if the command failed.  If
/// command-specific return codes are needed, they should be returned as
/// command-specific fields instead of returning them here so that there
/// is no risk of overlapping return codes.
///
/// It is expected that at some point prior to calling this function the
/// message was initialized with a call to \a ipc_init_msg or
/// \a ipc_init_msgq_msg.
///
/// Once a message has been sent it is not safe to send again until it has been
/// sent back to the sender as a response.
///
/// The following return codes are possible:
///
/// \retval IPC_RC_SUCCESS The message was successfully placed on the target's
/// receive buffer.
///
/// \retval IPC_RC_MSG_NOT_ACTIVE The message is not from an active command.
///
/// \retval IPC_RC_INVALID_TARGET_ID The target id for the sender is invalid.
/// This likely means that the message has been corrupted.
///
/// \retval IPC_RC_BUFFER_FULL The response could not be sent because the target's
/// recieve buffer is full.
///
/// \retval IPC_RC_TARGET_BLOCKED The response could not be sent because the
/// target is blocking any new messages.
///
int ipc_send_rsp(ipc_msg_t* rsp, uint32_t return_code);


///////////////////////////////////////////////////////////////////////////////
/// Retrieves the IPC return code embedded in the response message.
///
/// \param rsp A pointer to a response message.
///
/// The embedded IPC return code is how the remote processor communicates
/// IPC internal failures to the local processor.  It can also be used by
/// non-IPC code on the remote processor to signal success or failure when it
/// sends the response message via the \a ipc_send_rsp interface.
///
/// The IPC return code should always be checked to verify that a command
/// message was processed successfully.
///
/// The following return codes are possible:
///
/// \retval IPC_RC_SUCCESS The message was successfully processed.
///
/// \retval IPC_RC_CMD_FAILED The command was processed on the remote end but
/// did not complete successfully.
///
/// \retval IPC_RC_CMD_NOT_SUPPORTED The function ID for the command is valid
/// but the remote end does not have support for that function ID.
///
/// \retval IPC_RC_INVALID_FUNC_ID The function ID for the command is invalid.
///
static inline int ipc_get_rc(ipc_msg_t* rsp)
{
    return rsp->ipc_rc;
}

///////////////////////////////////////////////////////////////////////////////
/// Retrieve the IPC function ID for a message
///
/// \param msg A pointer to an IPC message
///
/// This interface should be used to extract the IPC function ID of a message.
///
/// The IPC function ID is returned.
///
static inline int ipc_get_funcid(ipc_msg_t* msg)
{
    int func_id = msg->func_id.word32;

    //Multi-target function ID's always have the target ID set to
    //so that if the caller doesn't set it to a proper target id it will
    //flag an error.
    if(func_id & IPC_FLAG_MT)
    {
        func_id |= IPC_TARGET_MASK;
    }

    //Clear the active and response flags in case they are set along
    //with the sender ID.
    func_id &= ~(IPC_FLAG_ACTIVE | IPC_FLAG_RESPONSE | IPC_SENDER_MASK);

    return func_id;
}

///////////////////////////////////////////////////////////////////////////////
/// Determine if a message is a response or a command
///
/// \param msg A pointer to an IPC message
///
/// This function should be used to determine if a message is a response or a
/// command.  The function will return a non-zero value if the message is a
/// response and 0 otherwise.
///
static inline int ipc_is_a_response(ipc_msg_t* msg)
{
    return msg->func_id.response_flag;
}

///////////////////////////////////////////////////////////////////////////////
/// Determine if a message is free to be re-used
///
/// \param msg A pointer to an IPC message
///
/// This function should be used to determine if a message is free to re-use.
/// The function will return a non-zero value if the message is free and
/// 0 otherwise.
///
static inline int ipc_is_free(ipc_msg_t* msg)
{
    return !msg->func_id.active_flag;
}

///////////////////////////////////////////////////////////////////////////////
/// Retrieve the sender ID of a message.
///
/// \param msg A pointer to an IPC message
///
/// This function should be used to retrieve the sender ID of a message.  It
/// returns the sender ID.
///
static inline int ipc_sender_id(ipc_msg_t* msg)
{
    return msg->func_id.sender_id;
}

///////////////////////////////////////////////////////////////////////////////
/// Retrieve the target ID of a message.
///
/// \param msg A pointer to an IPC message
///
/// This function should be used to retrieve the target ID of a message.  It
/// returns the target ID.
///
static inline int ipc_target_id(ipc_msg_t* msg)
{
    return msg->func_id.target_id;
}

///////////////////////////////////////////////////////////////////////////////
/// Initializes IPC control structures.
///
/// Clears the IPC buffers for this processor and places them in a state
/// where new messages are blocked.  Also initializes the IPC handler table
/// for this processor if STATIC_IPC_TABLES has not been defined.
///
/// This function always returns \a IPC_RC_SUCCESS
///
int ipc_init(void);


///////////////////////////////////////////////////////////////////////////////
/// Enables IPC communications.
///
/// Unmasks the IPC interrupt for this processor and places the circular
/// buffers in a state where they can recieve messages.
///
/// This function must be called before using the \a ipc_send_cmd function or
/// it will return \a IPC_RC_SELF_BLOCKED.
///
/// The function always returns \a IPC_RC_SUCCESS.
///
int ipc_enable(void);


///////////////////////////////////////////////////////////////////////////////
/// Disable recieving new IPC commands for a processor.
///
/// \param target_id The target ID of the processor to disable receiving new
/// IPC commands on.
///
/// This interface should be used by a processor that knows it is about to go
/// down or by a processor that knows that one of its peers has halted.
///
/// Calling this function on a processor will cause other processors to get
/// a return code of \a IPC_RC_TARGET_BLOCKED with subsequent calls to
/// \a ipc_send_cmd.  Calling this function on one's self (using a target ID of
/// OCCHW_INST_ID_SELF) will cause subsequent calls to \a ipc_send_cmd to
/// return \a IPC_RC_SELF_BLOCKED.
///
/// Possible return codes for this function are:
///
/// \retval IPC_RC_SUCCESS The target processor was successfully disabled.
///
/// \retval IPC_RC_INVALID_TARGET_ID The target ID is invalid.
///
int ipc_disable(uint32_t target_id);

///////////////////////////////////////////////////////////////////////////////
/// Set the ipc irq handler done hook
///
/// \param func Function pointer to the done hook function
///
/// This interface should be used by a processor that wants to
/// hook into the IPC IRQ handler after all the messages in the buffer
/// have processed.
///
/// Possible return codes for this function are:
///
/// \retval IPC_RC_SUCCESS Function pointer was successfully set.
///
/// \retval IPC_RC_INVALID_FUNC_PTR Function pointer is NULL.
///
void ipc_set_done_hook(void* func);

///////////////////////////////////////////////////////////////////////////////
/// Associates an IPC function ID with a handler function
///
/// \param func_id A user-defined function ID that is known to both the sender
/// and receiver of a command message.  (Defined in \a ipc_func_ids.h)
///
/// \param handler A pointer to the function that handles command messages that
/// have been initialized with \a func_id.
///
/// \param callback_arg A pointer to data that is passed as an argument to the
/// handler function when it is called.
///
/// This function should be used to link an IPC function ID with a function.
/// Once this has been done, if the local processor recieves a command message
/// with an IPC function ID that matches \a func_id then it will call the
/// handler function that was specified by \a handler.
///
/// NOTE: All handler functions will be called from an interrupt context.
///
/// Possible return codes are:
///
/// \retval IPC_RC_SUCCESS The operation completed successfully.
///
/// \retval IPC_RC_INVALID_TARGET_ID The function ID is a single-target
/// function ID that does not target this processor.
///
/// \retval IPC_RC_INVALID_FUNC_ID The function ID is not a valid IPC function
/// ID.
///
/// \retval IPC_RC_INVALID_ARG The handler parameter must be a non-zero value.
///
int ipc_set_handler(uint32_t func_id,
                    ipc_msg_handler_t handler,
                    void* callback_arg);


///////////////////////////////////////////////////////////////////////////////
/// Initialize an IPC message queue.
///
/// \param msgq A pointer to a message queue.
///
/// All message queues must be initialized one time prior to use with other
/// interfaces.
///
void ipc_init_msgq(ipc_msgq_t* msgq);

//Use this to statically initialize an IPC message queue
#define IPC_MSGQ_CREATE(msgq) \
    ipc_msgq_t msgq = \
                      {\
                       .msg_head = KERN_DEQUE_SENTINEL_INIT(&msgq.msg_head),\
                       .msg_sem = KERN_SEMAPHORE_INITIALIZATION(0, 0)\
                      }

///////////////////////////////////////////////////////////////////////////////
/// Initialize an IPC message and associate it with an IPC message queue
///
/// \param msg A pointer to an IPC message.
///
/// \param func_id A user-defined function ID that is known to both the sender
/// and receiver of the command message.  (Defined in \a ipc_func_ids.h)
///
/// \param msgq A pointer to an initialized IPC message queue.
///
/// This interface should be used in place of \a ipc_init_msg when the caller
/// wishes to have the command response placed on the specified IPC message
/// queue.  This allows a thread to block (via the \a ipc_msq_recv interface)
/// on the message queue until a response to a command has arrived.
///
/// \note An IPC message queue can be associated with more than one IPC
/// message.
///
/// See \a ipc_init_msg for more information.
///
void ipc_init_msgq_msg(ipc_msg_t* msg, uint32_t func_id, ipc_msgq_t* msgq);

//Use this to statically create an initialized IPC message queue message
#define IPC_MSGQ_MSG_CREATE(msg_name, func_id, msgq) \
    IPC_MSG_CREATE(msg_name, func_id, ipc_msgq_handler, msgq)

///////////////////////////////////////////////////////////////////////////////
/// Wait (with timeout) for an IPC message on an IPC message queue.
///
/// \param msg A pointer to an IPC message pointer.
///
/// \param msgq A pointer to an initialized IPC message queue.
///
/// \param timeout The time to wait for the next IPC message to arrive.
///
/// This interface can be used in a thread context to block while waiting for
/// the next message (command or response) to arrive on an IPC message queue.
///
/// For an IPC command message to be placed on an IPC message queue, the
/// function ID for the command must first be associated with the message queue
/// by making a call to \a ipc_register_msgq.
///
/// For an IPC response message to be placed on an IPC message queue, the
/// message must be initialized via the \a ipc_init_msgq_msg interface.
///
/// If \a ipc_msgq_recv returns a value of \a IPC_RC_SUCCESS then \a msg is
/// guaranteed to point to a new message.  Otherwise, an IPC message was not
/// retrieved from the message queue and \a msg will be set to 0.
///
/// Possible return codes for this function are:
///
/// \retval IPC_RC_SUCCESS A new IPC message was received and \a msg has been
/// set to point to the new message.
///
/// \retval IPC_RC_TIMEOUT No new messages were recieved within the timeout
/// period and \msg was set to 0.
///
/// \retval IPC_RC_NO_MSG This should never happen, but if it does it indicates
/// an internal failure occurred.
///
int ipc_msgq_recv(ipc_msg_t** msg, ipc_msgq_t* msgq, KERN_INTERVAL timeout);


///////////////////////////////////////////////////////////////////////////////
/// Associate an IPC message queue with an IPC function ID
///
/// \param func_id A user-defined function ID that is known to both the sender
/// and receiver of a command message.  (Defined in \a ipc_func_ids.h)
///
/// \param msgq A pointer to an initialized IPC message queue.
///
/// This interface associates an IPC function ID with an IPC message queue so
/// that when the calling processor recieves a command with the specified
/// function ID the message will be place on the message queue and a thread
/// that is blocked waiting for a message on the queue (using the
/// \a ipc_msgq_recv interface) will be woken up and given the message.
///
/// This function should be called in place of the \a ipc_set_handler
/// interface.
///
/// NOTE: Multiple function ID's can be associated with a single queue.
///
/// Possible return codes are:
///
/// \retval IPC_RC_SUCCESS The operation completed successfully.
///
/// \retval IPC_RC_INVALID_TARGET_ID The function ID is a single-target
/// function ID that does not target this processor.
///
/// \retval IPC_RC_INVALID_FUNC_ID The function ID is not a valid IPC function
/// ID.
///
int ipc_register_msgq(uint32_t func_id, ipc_msgq_t* msgq);

///////////////////////////////////////////////////////////////////////////////
/// Internal function that places an IPC message on an IPC message queue
///
void ipc_msgq_handler(ipc_msg_t* msg, void* arg);

///////////////////////////////////////////////////////////////////////////////
/// The default IPC command handler simply sends a response with the IPC return
/// code set to IPC_RC_CMD_NOT_SUPPORTED
///
void ipc_default_handler(ipc_msg_t* msg, void* arg);

#endif /*__ASSEMBLER__*/
#endif  /* __IPC_API_H__ */
