Linux IPI Mailbox Driver
Introduction
This page gives an overview of IPI Mailbox driver which is available as part of the Xilinx Linux distribution or Open source linux as drivers/mailbox/zynqmp-ipi-mailbox.c
This information corresponds to the IPI Mailbox driver that is the main branch of the Git tree.
This driver supports IPI in ZynqMP, Versal Gen 1 and Versal Gen 2 devices.
HW/IP Features
Cross-processor communication interrupts with message passing
Source agent (a processor)
Destination agent (a processor)
Number of Agents:
Three hardwired agents: PMC with and without a message buffer, and PSM with buffer
Five software selectable agents with message buffer
One software selectable agent without message buffer
Agent assignments
System management ID (SMID) tags in the IPI registers are used for message buffer and interrupt register access
System interrupt routing
IPI agent interrupt registers
Protected by 64 KB apertures in LPD_XPPU
IPI software message filtering
Message buffers are protected by hardware, and aperture controls using the SMIDs
64 request message buffers, 32 bytes each
64 response message buffers, 32 bytes each
Features supported in driver
Supports cross-processor communication and message passing using IPI hardware.
Allows configuration of source and destination agents (processors) for communication.
Handles multiple IPI agents, including both hardwired and software-selectable agents.
Manages both transmit (TX) and receive (RX) mailbox channels for each agent.
Integrates with the Linux mailbox framework, providing standard mailbox controller and channel operations.
Uses ARM SMC or HVC calls to interact with platform firmware for mailbox operations.
Handles IPI interrupts, including both system IRQs and per-CPU SGIs, with proper registration and cleanup.
Validates message sizes against hardware buffer limits (typically 32 bytes per buffer).
Configures all agent IDs, buffer regions, and interrupts via device tree bindings for flexible integration.
Supports CPU hotplug events and proper resource management, including per-CPU IRQ handling and dynamic device registration/unregistration.
Missing features, Known Issues and Limitations
Advanced software message filtering is not implemented; only basic message checks are performed.
Fine-grained SMID-based access control and aperture configuration for message buffers and interrupt registers are not managed by the driver.
The driver does not configure or manage the 64 KB LPD_XPPU apertures for IPI register protection; this is assumed to be handled by hardware or firmware.
Dynamic agent assignment or runtime reconfiguration of agent IDs is not supported; agent IDs are fixed via device tree.
The driver does not provide APIs to enumerate, allocate, or manage all 64 request/response buffers in hardware.
Explicit support for multiple bufferless agents or mixed buffer/bufferless configurations is limited.
Not all IPI hardware features (such as acceptance filters, advanced interrupt routing, or buffer protection settings) are exposed to user space or higher-level software.
Fine-grained or programmable interrupt routing between agents is not provided by the driver.
Programmable acceptance filters for message reception are not implemented.
Detailed error reporting, logging, and diagnostics for all IPI hardware events are not fully supported.
Kernel Configuration
Symbol: ZYNQMP_IPI_MBOX [=y] │
│ Type : tristate │
│ Defined at drivers/mailbox/Kconfig:253 │
│ Prompt: Xilinx ZynqMP IPI Mailbox │
│ Depends on: MAILBOX [=y] && ARCH_ZYNQMP [=y] && OF [=y] │
│ Location: │
│ -> Device Drivers │
│ -> Mailbox Hardware Support (MAILBOX [=y]) │
│ (1) -> Xilinx ZynqMP IPI Mailbox (ZYNQMP_IPI_MBOX [=y]) │
│ Selected by [y]: │
│ - XLNX_R5_REMOTEPROC [=y] && REMOTEPROC [=y] && PM [=y] && ARCH_ZYNQMP [=y] │
│ - ZYNQMP_POWER [=y] && PM [=y] && ZYNQMP_FIRMWARE [=y]
The driver is available at,
https://github.com/Xilinx/linux-xlnx/blob/master/drivers/mailbox/zynqmp-ipi-mailbox.c
Device Tree
For More details about the device tree bindings doc please refer to the Documentation/devicetree/bindings/mailbox/xlnx%2Czynqmp-ipi-mailbox.yaml
For ZynqMP:
amba {
#address-cells = <0x2>;
#size-cells = <0x2>;
zynqmp-mailbox {
compatible = "xlnx,zynqmp-ipi-mailbox";
interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
xlnx,ipi-id = <0>;
#address-cells = <2>;
#size-cells = <2>;
ranges;
mailbox: mailbox@ff9905c0 {
compatible = "xlnx,zynqmp-ipi-dest-mailbox";
reg = <0x0 0xff9905c0 0x0 0x20>,
<0x0 0xff9905e0 0x0 0x20>,
<0x0 0xff990e80 0x0 0x20>,
<0x0 0xff990ea0 0x0 0x20>;
reg-names = "local_request_region",
"local_response_region",
"remote_request_region",
"remote_response_region";
#mbox-cells = <1>;
xlnx,ipi-id = <4>;
};
};
};
For Versal:
bus {
#address-cells = <2>;
#size-cells = <2>;
mailbox@ff300000 {
compatible = "xlnx,versal-ipi-mailbox";
interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <2>;
#size-cells = <2>;
reg = <0x0 0xff300000 0x0 0x1000>,
<0x0 0xff990000 0x0 0x1ff>;
reg-names = "ctrl", "msg";
xlnx,ipi-id = <0>;
ranges;
/* buffered IPI */
mailbox@ff340000 {
compatible = "xlnx,versal-ipi-dest-mailbox";
reg = <0x0 0xff340000 0x0 0x1000>,
<0x0 0xff990400 0x0 0x1ff>;
reg-names = "ctrl", "msg";
#mbox-cells = <1>;
xlnx,ipi-id = <4>;
};
/* bufferless IPI */
mailbox@ff370000 {
compatible = "xlnx,versal-ipi-dest-mailbox";
reg = <0x0 0xff370000 0x0 0x1000>;
reg-names = "ctrl";
#mbox-cells = <1>;
xlnx,ipi-id = <7>;
};
};
};
Test Procedure
IPI Mailbox Client Driver Example
struct ipi_mbox_demo_client {
struct mbox_client cl;
struct mbox_chan *mchan;
struct completion c;
/* ... */
};
/*
* This is the rx callback for data (data in IPI source request buffer) received from remote.
*/
static void ipi_rx_callback(struct mbox_client *cl, void *mssg)
{
struct ipi_mbox_demo_client *dc = container_of(cl, struct ipi_mbox_demo_client, cl);
/* A new message is received, depends on your application, handler the received data. */
queue_req(mssg);
}
/*
* This is tx done callback. This is only required if the user needs to be notified if the message
* has been sent.
*/
static void ipi_tx_done(struct mbox_client *cl, void *active_request, int r)
{
struct ipi_mbox_demo_client *dc = container_of(cl, struct ipi_mbox_demo_client, cl);
/* In case you want to get the IPI response from the IPI response buffer */
struct ipi_mbox_channel *ipi_chnl = (struct ipi_mbox_channel *)dc->mchan->con_priv;
/* active_request indicates which request has been sent */
if (!active_request) {
/* get the IPI response from ipi_chnl response data */
kfree(active_request);
}
complete(&dc->c);
}
static void ipi_mbox_client_demo()
{
struct ipi_mbox_demo_client *dc;
uint8_t *data;
dc = kzalloc(sizeof(*dc), GFP_KERNEL);
data = kzalloc(sizeof(*dc), GFP_KERNEL);
#ifdef TX_DONE_REQUESTED
/* Populate client needs to know when TX is done */
dc->cl.dev = &pdev->dev;
dc->cl.rx_callback = ipi_rx_callback;
dc->cl.tx_done = ipi_tx_done;
dc->cl.tx_block = true;
dc->cl.tx_tout = 1; /* by 1 miniseconds */
dc->cl.knows_txdone = false;
/* mailbox is listed second in 'mboxes' property */
dc->mchan = mbox_request_channel_byname(&dc->cl, "rpu0");
mbox_send_message(dc_async->mchan, &data);
/* If I just want to kick without message */
mbox_send_message(dc_async->mchan, NULL);
/* Now wait for async chan to be done */
wait_for_completion(&dc_async->c);
#else
/* Populate client doesn't need to know when TX is done */
dc->cl.dev = &pdev->dev;
dc->cl.rx_callback = ipi_rx_callback;
dc->cl.tx_done = NULL;
dc->cl.tx_block = false;
dc->cl.tx_tout = 0; /* Doesn't matter in this case */
dc->cl.knows_txdone = false;
/* Async mailbox is listed second in 'mboxes' property */
dc->mchan = mbox_request_channel_byname(&dc->cl, "rpu1");
mbox_send_message(dc->mchan, &data);
/* If I just want to kick without message */
mbox_send_message(dc->mchan, NULL);
#endif
}
IPI MailBox Driver Userspace APIs
ioctl APIs for channel requesting.
when requesting an IPI channel, it will do the following verification
target cannot be PMU
target cannot be already requested
If it has passed verification, it will
create a char device for the requested channel
create a mbox client for the requested channel
the mbox client will register rx_callback, the rx_callback will put the received data into the socket buffer queue
each API channel is a char device
open()
request the IPI mailbox channel
release()
shutdown the IPI mailbox channel
wirte()
len > 0, it will write data to IPI request buffer, if it is more than 32bytes, it will only write the first 32bytes to the IPI request buffer, and set IPI target bit of the trigger register.
len == 0, it will set IPI target bit of trigger register.
read()
copy data from the received IPI request data socket buffer queue if there is data ready.
Mainline status
This driver is currently in sync with mainline kernel driver.
Change Log
2025.1
2024.2
2024.1
2023.2
None
© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy