Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boards/raspberrypi/rpi_pico2/rpi_pico2.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
zephyr,flash-controller = &qmi;
zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
zephyr,ipc = &ipc;
};

aliases {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_USE_DT_CODE_PARTITION=y
CONFIG_MBOX=y
1 change: 1 addition & 0 deletions drivers/mbox/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ zephyr_library_sources_ifdef(CONFIG_MBOX_TI_OMAP_MAILBOX mbox_ti_omap.c)
zephyr_library_sources_ifdef(CONFIG_MBOX_RENESAS_RZ_MHU mbox_renesas_rz_mhu.c)
zephyr_library_sources_ifdef(CONFIG_MBOX_MHUV3 mbox_mhuv3.c)
zephyr_library_sources_ifdef(CONFIG_MBOX_TI_SECURE_PROXY mbox_ti_secproxy.c)
zephyr_library_sources_ifdef(CONFIG_MBOX_RPI_PICO mbox_rpi_pico.c)
1 change: 1 addition & 0 deletions drivers/mbox/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ source "drivers/mbox/Kconfig.ti_omap"
source "drivers/mbox/Kconfig.renesas_rz"
source "drivers/mbox/Kconfig.mhuv3"
source "drivers/mbox/Kconfig.ti_secproxy"
source "drivers/mbox/Kconfig.rpi_pico"

config MBOX_INIT_PRIORITY
int "MBOX init priority"
Expand Down
10 changes: 10 additions & 0 deletions drivers/mbox/Kconfig.rpi_pico
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2025 Igalia S.L.
# SPDX-License-Identifier: Apache-2.0

config MBOX_RPI_PICO
bool "Inter-processor mailbox driver for the RP2350/RP2040 SoCs"
default y
depends on DT_HAS_RASPBERRYPI_PICO_MBOX_ENABLED
help
Raspberry Pi Pico mailbox driver based on the RP2350/RP2040
inter-processor FIFOs.
193 changes: 193 additions & 0 deletions drivers/mbox/mbox_rpi_pico.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Copyright (c) 2025 Igalia S.L.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/logging/log.h>
#include <zephyr/drivers/mbox.h>
#include <zephyr/devicetree.h>
#include <zephyr/irq.h>
/* pico-sdk includes */
#include <hardware/structs/sio.h>


#define DT_DRV_COMPAT raspberrypi_pico_mbox

LOG_MODULE_REGISTER(mbox_rpi_pico, CONFIG_MBOX_LOG_LEVEL);

#define sev() __asm__ volatile("sev" : : : "memory")
#define MAILBOX_MBOX_SIZE sizeof(uint32_t)
#define MAILBOX_DEV_NAME mbox0

struct rpi_pico_mailbox_data {
const struct device *dev;
mbox_callback_t cb;
void *user_data;
};

static struct rpi_pico_mailbox_data rpi_pico_mbox_data;


/*
* Clears the ROE and WOF flags, if set.
*/
static inline void fifo_clear_status(void)
{
sio_hw->fifo_st = 0xff;
}

/*
* Returns true if the write FIFO isn't full. Returns false otherwise.
*/
static inline bool fifo_write_ready(void)
{
return sio_hw->fifo_st & SIO_FIFO_ST_RDY_BITS;
}

/*
* Returns true if the read FIFO has data available, ie. sent by the
* other core. Returns false otherwise.
*/
static inline bool fifo_read_valid(void)
{
return sio_hw->fifo_st & SIO_FIFO_ST_VLD_BITS;
}

/*
* Discard any data in the read FIFO.
*/
static inline void fifo_drain(void)
{
while (fifo_read_valid()) {
(void)sio_hw->fifo_rd;
}
}

static int rpi_pico_mbox_send(const struct device *dev,
uint32_t channel, const struct mbox_msg *msg)
{
ARG_UNUSED(dev);
ARG_UNUSED(channel);

if (!fifo_write_ready()) {
return -EBUSY;
}
if (msg->size > MAILBOX_MBOX_SIZE) {
return -EMSGSIZE;
}
LOG_DBG("CPU %d: send IP data: %d", sio_hw->cpuid, *((int *)msg->data));
sio_hw->fifo_wr = *((uint32_t *)(msg->data));
Comment on lines +79 to +80
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably send the channel number here. Most consumers expect more than one channel, but do not send data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case the mailbox has only one channel from the point of view of the core that sends the message, right? Actually, the concept of "channel" isn't used in the driver at all.

But yes, it still may be useful to add it to the debug message to know how the caller called the function.

Copy link
Member

@dsseng dsseng Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant for consumers it'll probably be better if the mailbox provided more channels, rather than data transfer.

What are you using it for? I use IPM in my branch for IPC subsystem, and so I need more than one channel, but signaling only, no data (data resides in shared memory and access is controlled by two mailbox channels)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're talking about different things here...

What I did here is a direct mapping of the hardware functionality, with no further abstractions. This handles the SIO mailboxes. According to the docs, there are only two FIFOs (let's call them A and B), one core can only read from A and write to B, while the other can only read from B and write to A. From the point of view of each of the cores, there's only one single channel. Unless we mean different things when we say "channel" in this context.

When you say the data resides in shared memory and the access is controlled by the mailbox channels, I understand that's handled by the IPC mechanism you implemented on top of the mailboxes (ie. a region of memory shared between the cores and a signalling mechanism using the mailboxes). When I mention "data" in the driver, I mean the data that goes through the mailboxes. In the RP2350 each mailbox can hold 4 4-byte messages, for instance. You can ignore the data and use a mailbox for signalling events only, or you can use it to signal an event and carry some data with it (a memory address, for instance).

In the end, the use case will be inter-processor communication, but what I'm doing here is to provide low-level access to the hardware peripheral so that the IPM can be implemented on top of it. For example, using zephyr,mbox-imp as an adaptor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, let's leave this to maintainers, because that's really use-case dependent

sev();

return 0;
}

static int rpi_pico_mbox_register_callback(const struct device *dev,
uint32_t channel,
mbox_callback_t cb,
void *user_data)
{
ARG_UNUSED(channel);

struct rpi_pico_mailbox_data *data = dev->data;
uint32_t key;

key = irq_lock();
data->cb = cb;
data->user_data = user_data;
irq_unlock(key);

return 0;
}

static int rpi_pico_mbox_mtu_get(const struct device *dev)
{
ARG_UNUSED(dev);

return MAILBOX_MBOX_SIZE;
}

static uint32_t rpi_pico_mbox_max_channels_get(const struct device *dev)
{
ARG_UNUSED(dev);

/* Only one channel per CPU supported. */
return 1;
}

static int rpi_pico_mbox_set_enabled(const struct device *dev,
uint32_t channel, bool enable)
{
ARG_UNUSED(dev);
ARG_UNUSED(channel);

if (enable) {
irq_enable(DT_INST_IRQ_BY_NAME(0, MAILBOX_DEV_NAME, irq));
} else {
irq_disable(DT_INST_IRQ_BY_NAME(0, MAILBOX_DEV_NAME, irq));
}

return 0;
}

static void rpi_pico_mbox_isr(const struct device *dev)
{
struct rpi_pico_mailbox_data *data = dev->data;

/*
* Ignore the interrupt if it was triggered by anything that's
* not a FIFO receive event.
*
* NOTE: the interrupt seems to be triggered when it's first
* enabled even when the FIFO is empty.
*/
if (!fifo_read_valid()) {
LOG_DBG("Interrupt received on empty FIFO: ignored.");
return;
}

if (data->cb != NULL) {
uint32_t d = sio_hw->fifo_rd;
struct mbox_msg msg = {
.data = &d,
.size = sizeof(d)};
data->cb(dev, 0, data->user_data, &msg);
}
fifo_drain();
}

static int rpi_pico_mbox_init(const struct device *dev)
{
ARG_UNUSED(dev);

LOG_DBG("Initial FIFO status: 0x%x", sio_hw->fifo_st);
LOG_DBG("FIFO depth: %d", DT_INST_PROP(0, fifo_depth));
irq_disable(DT_INST_IRQ_BY_NAME(0, MAILBOX_DEV_NAME, irq));
fifo_drain();
fifo_clear_status();
LOG_DBG("FIFO status after setup: 0x%x", sio_hw->fifo_st);
IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, MAILBOX_DEV_NAME, irq),
DT_INST_IRQ_BY_NAME(0, MAILBOX_DEV_NAME, priority),
rpi_pico_mbox_isr, DEVICE_DT_INST_GET(0), 0);

return 0;
}

static DEVICE_API(mbox, rpi_pico_mbox_driver_api) = {
.send = rpi_pico_mbox_send,
.register_callback = rpi_pico_mbox_register_callback,
.mtu_get = rpi_pico_mbox_mtu_get,
.max_channels_get = rpi_pico_mbox_max_channels_get,
.set_enabled = rpi_pico_mbox_set_enabled,
};

DEVICE_DT_INST_DEFINE(
0,
rpi_pico_mbox_init,
NULL,
&rpi_pico_mbox_data,
NULL,
POST_KERNEL,
CONFIG_MBOX_INIT_PRIORITY,
&rpi_pico_mbox_driver_api);
28 changes: 28 additions & 0 deletions dts/arm/raspberrypi/rpi_pico/rp2040.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,34 @@
alarms-count = <1>;
status = "disabled";
};

sio: sio@d0000000 {
compatible = "raspberrypi,pico-sio";
reg = <0xd0000000 0x180>;

mbox: mbox {
compatible = "raspberrypi,pico-mbox";
interrupts = <15 RPI_PICO_DEFAULT_IRQ_PRIORITY>,
<16 RPI_PICO_DEFAULT_IRQ_PRIORITY>;
interrupt-names = "mbox0", "mbox1";
fifo-depth = <8>;
#mbox-cells = <0>;
status = "okay";
};
};

ipc: ipc {
/*
* The zephyr,mbox-ipm driver expects two mailbox
* channels, but the mailbox block in the RP2040
* implements a single instance per core. Point the
* two driver channels to the same single mbox.
*/
compatible = "zephyr,mbox-ipm";
mboxes = <&mbox>, <&mbox>;
mbox-names = "tx", "rx";
status = "okay";
};
};

pinctrl: pin-controller {
Expand Down
27 changes: 27 additions & 0 deletions dts/arm/raspberrypi/rpi_pico/rp2350.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,33 @@
interrupt-names = "irq0", "irq1";
status = "disabled";
};

sio: sio@d0000000 {
compatible = "raspberrypi,pico-sio";
reg = <0xd0000000 DT_SIZE_K(80)>;

mbox: mbox {
compatible = "raspberrypi,pico-mbox";
interrupts = <25 RPI_PICO_DEFAULT_IRQ_PRIORITY>;
interrupt-names = "mbox0";
fifo-depth = <4>;
#mbox-cells = <0>;
status = "okay";
};
};

ipc: ipc {
/*
* The zephyr,mbox-ipm driver expects two mailbox
* channels, but the mailbox block in the RP2350
* implements a single instance per core. Point the
* two driver channels to the same single mbox.
*/
compatible = "zephyr,mbox-ipm";
mboxes = <&mbox>, <&mbox>;
mbox-names = "tx", "rx";
status = "okay";
};
};

pinctrl: pin-controller {
Expand Down
14 changes: 14 additions & 0 deletions dts/bindings/mbox/raspberrypi,pico-mbox.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) 2025 Igalia S.L.
# SPDX-License-Identifier: Apache-2.0

description: Raspberry Pi Pico interprocessor mailbox

compatible: "raspberrypi,pico-mbox"

include: [base.yaml, mailbox-controller.yaml]

properties:
fifo-depth:
type: int
description: number of entries that the mailbox FIFO can hold
required: true
8 changes: 8 additions & 0 deletions dts/bindings/misc/raspberrypi,pico-sio.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2025 Igalia S.L.
# SPDX-License-Identifier: Apache-2.0

description: Raspberry Pi Pico SIO

compatible: "raspberrypi,pico-sio"

include: base.yaml