zephyr/include/ipi.h
Andrew Boie faaab4831f ipi.h: introduce low-level inter-processor interrupt API
This interface is an abstraction for sending messages between
processors with irq-context callbacks and no definition of
protocol.

We assume that for all IPC implementations, we can send over a
message identifier and a sized data payload, and that the receiving
side gets an interrupt on an incoming message. APIs exist to indicate
the max value of the message id and how much data can be sent over a
single message.

This will be a foundation to build various high-level drivers
which can be bound at runtime to this low-level interface. Some
examples:
* IPC where IPC messages are used as a doorbell to signal new data in
  a shared queue
* IPC with various kinds of message buffering and deferred message
  processing  via semaphores
* IPC for serial console debug output, either a byte or a message at
  a time
* IPC over UARTS in a non-shared memory environment where large-ish
  messages are exchanged
* Shared memory IPC where data pointers are exchanged and a protocol
  for freeing the data once the receiver is done with it

The size parameter passed to ipc_send() isn't propagated to the
receiving side. The receiver needs to infer how many bytes of
data to read in the callback function based on the protocol implemented
in the high-level driver; it can be based on message ID, or bits reserved
in the message ID for the message size, or some other mechanism.

Change-Id: I9a9529beb61cdebdfb1bafd29d037f926fab3c1b
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2016-02-05 20:24:17 -05:00

179 lines
6.3 KiB
C

/* ipi.h - Generic low-level inter-processor interrupt communication API */
/*
* Copyright (c) 2015 Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1) Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3) Neither the name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __INCipih
#define __INCipih
#include <nanokernel.h>
#include <device.h>
/**
* Callback API for incoming IPI messages
*
* These callbacks are executed in interrupt context, and only interrupt-
* safe APIS may be used. Registration of callbacks is done via
* ipi_register_callback()
*
* @param context Arbitrary context pointer provided at registration time
* @param id Message type identifier
* @param data Message data pointer. The correct amount of data to read out
* needs to be inferred by the message id/upper level protocol
*/
typedef void (*ipi_callback_t)(void *context, uint32_t id, volatile void *data);
typedef int (*ipi_send_t)(struct device *ipidev, int wait, uint32_t id,
const void *data, int size);
typedef int (*ipi_max_data_size_get_t)(struct device *ipidev);
typedef uint32_t (*ipi_max_id_val_get_t)(struct device *ipidev);
typedef void (*ipi_register_callback_t)(struct device *port, ipi_callback_t cb,
void *cb_context);
typedef int (*ipi_set_enabled_t)(struct device *ipidev, int enable);
struct ipi_driver_api {
ipi_send_t send;
ipi_register_callback_t register_callback;
ipi_max_data_size_get_t max_data_size_get;
ipi_max_id_val_get_t max_id_val_get;
ipi_set_enabled_t set_enabled;
};
/**
* Try to send a message over the IPI device
*
* @param ipidev Driver instance
* @param wait If nonzero, busy-wait for remote to consume the message. The
* message is considered consumed once the remote interrupt handler
* finishes. If there is deferred processing on the remote side,
* or you would like to queue outgoing messages and wait on an
* event/semaphore, you can implement that in a high-level driver
* @param id Message identifier. Values are constrained by
* ipi_max_data_size_get() since many platforms only allow for a
* subset of bits in a 32-bit register to store the ID.
* @param data Pointer to data to send in the message
* @param size size of the data pointed to
*
* There are constraints on how much data can be sent or the maximum value
* of id, use the ipi_max_data_size_get() and ipi_max_id_val_get() functions
* to determine these.
*
* The size parameter is only used on the sending side to know how much data
* to put in the message registers, it is not passed along to the receiving
* side. How much data to read back should be dictated by the upper-level
* protocol.
*
* @return -EBUSY if the remote hasn't yet read the last data sent
* -EMSGSIZE If the supplied data size is unsupported by the driver
* -EINVAL Bad parameter, such as a too-large id value, or
* the device isn't an outbound IPI channel
* 0 Success
*/
static inline int ipi_send(struct device *ipidev, int wait, uint32_t id,
void *data, int size)
{
struct ipi_driver_api *api;
api = (struct ipi_driver_api *) ipidev->driver_api;
return api->send(ipidev, wait, id, data, size);
}
/**
* Register a callback function for incoming messages
*
* @param ipidev Driver instance
* @param cb Callback function to execute on incoming message interrupts
* @param context Application-specific context pointer which will be passed
* to the callback function when executed.
*/
static inline void ipi_register_callback(struct device *ipidev,
ipi_callback_t cb, void *context)
{
struct ipi_driver_api *api;
api = (struct ipi_driver_api *) ipidev->driver_api;
api->register_callback(ipidev, cb, context);
}
/**
* Return the maxmimum number of bytes that can be sent in an outbound message
*
* IPI implementations vary in how much data can be sent in a single message
* since the data payload is typically stored in registers.
*
* @param ipidev Driver instance pointer
*
* @return Max size of a message in bytes
*/
static inline int ipi_max_data_size_get(struct device *ipidev)
{
struct ipi_driver_api *api;
api = (struct ipi_driver_api *) ipidev->driver_api;
return api->max_data_size_get(ipidev);
}
/**
* Return the maximum id value that can be sent in an outbound message
*
* Many IPI implementations store the id in a register which may have some bits
* reserved for other use
*
* @param ipidev Driver instance pointer
*
* @return Maximum value of a message id
*/
static inline uint32_t ipi_max_id_val_get(struct device *ipidev)
{
struct ipi_driver_api *api;
api = (struct ipi_driver_api *) ipidev->driver_api;
return api->max_id_val_get(ipidev);
}
/**
* For inbound channels, enable interrupts/callbacks
*
* @param enabled 0=disable nonzero=enable
*
* @return 0 on success, -EINVAL if this isn't an inbound channel
*/
static inline int ipi_set_enabled(struct device *ipidev, int enable)
{
struct ipi_driver_api *api;
api = (struct ipi_driver_api *) ipidev->driver_api;
return api->set_enabled(ipidev, enable);
}
#endif /* __INCipih */