Re: [PATCH v2 2/3] mailbox: exynos: Add support for Exynos850 mailbox

Alexey Klimov
2026-04-08T13:08:11Z

Hi Tudor,

On Thu Apr 2, 2026 at 9:42 AM BST, Tudor Ambarus wrote:
> Hi, Alexey,
>
> On 4/2/26 5:20 AM, Alexey Klimov wrote:
>> Exynos850-based platforms support ACPM and has similar workflow
>> of communicating with ACPM via mailbox, however mailbox controller
>> registers are located at different offsets and writes/reads could be
>> different. To distinguish between such different behaviours,
>> the registers offsets for Exynos850 and the platform-specific data
>> structs are introduced and configuration is described in such structs
>> for gs101 and exynos850 based SoCs. Probe routine now selects the
>> corresponding platform-specific data via device_get_match_data().
>>
>> Signed-off-by: Alexey Klimov <alexey.klimov@linaro.org>
>> ---
>> drivers/mailbox/exynos-mailbox.c | 67 ++++++++++++++++++++++++++++++++++++++--
>> 1 file changed, 64 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/mailbox/exynos-mailbox.c b/drivers/mailbox/exynos-mailbox.c
>> index d2355b128ba4..f9c59c07558a 100644
>> --- a/drivers/mailbox/exynos-mailbox.c
>> +++ b/drivers/mailbox/exynos-mailbox.c
>> @@ -31,14 +31,61 @@
>>
>> #define EXYNOS_MBOX_CHAN_COUNT HWEIGHT32(EXYNOS_MBOX_INTGR1_MASK)
>>
>> +#define EXYNOS850_MBOX_MCUCTRL 0x0 /* Mailbox Control Register */
>> +#define EXYNOS850_MBOX_INTGR0 0x8 /* Interrupt Generation Register 0 */
>> +#define EXYNOS850_MBOX_INTCR0 0x0C /* Interrupt Clear Register 0 */
>> +#define EXYNOS850_MBOX_INTMR0 0x10 /* Interrupt Mask Register 0 */
>> +#define EXYNOS850_MBOX_INTSR0 0x14 /* Interrupt Status Register 0 */
>> +#define EXYNOS850_MBOX_INTMSR0 0x18 /* Interrupt Mask Status Register 0 */
>> +#define EXYNOS850_MBOX_INTGR1 0x1C /* Interrupt Generation Register 1 */
>> +#define EXYNOS850_MBOX_INTMR1 0x24 /* Interrupt Mask Register 1 */
>> +#define EXYNOS850_MBOX_INTSR1 0x28 /* Interrupt Status Register 1 */
>> +#define EXYNOS850_MBOX_INTMSR1 0x2C /* Interrupt Mask Status Register 1 */
>> +#define EXYNOS850_MBOX_VERSION 0x70
>
> Please consider defining just the registers that are used, to not
> pollute the driver. You may drop the unused gs101 definitions too.

Sure. Thanks. I was surprised how many unused defines were introduced
by gs101 SoC in the first place all over.

>> +#define EXYNOS850_MBOX_INTMR1_MASK GENMASK(15, 0)
>> +
>> +/**
>> + * struct exynos_mbox_driver_data - platform-specific mailbox configuration.
>> + * @irq_doorbell_offset: offset to the IRQ generation register, doorbell
>> + * to APM co-processor.
>> + * @irq_doorbell_shift: shift to apply to the value written to IRQ
>> + * generation register.
>> + * @irq_mask_offset: offset to the IRQ mask register.
>> + * @irq_mask_value: value to right to the mask register to mask out
>> + * all interrupts.
>> + */
>> +struct exynos_mbox_driver_data {
>> + u16 irq_doorbell_offset;
>> + u16 irq_doorbell_shift;
>> + u16 irq_mask_offset;
>> + u16 irq_mask_value;
>> +};
>> +
>> /**
>> * struct exynos_mbox - driver's private data.
>> * @regs: mailbox registers base address.
>> * @mbox: pointer to the mailbox controller.
>> + * @data: pointer to driver platform-specific data.
>> */
>> struct exynos_mbox {
>> void __iomem *regs;
>> struct mbox_controller *mbox;
>> + const struct exynos_mbox_driver_data *data;
>> +};
>> +
>> +static const struct exynos_mbox_driver_data exynos850_mbox_data = {
>> + .irq_doorbell_offset = EXYNOS850_MBOX_INTGR0,
>> + .irq_doorbell_shift = 16,
>> + .irq_mask_offset = EXYNOS850_MBOX_INTMR1,
>> + .irq_mask_value = EXYNOS850_MBOX_INTMR1_MASK,
>> +};
>> +
>> +static const struct exynos_mbox_driver_data exynos_gs101_mbox_data = {
>> + .irq_doorbell_offset = EXYNOS_MBOX_INTGR1,
>> + .irq_doorbell_shift = 0,
>> + .irq_mask_offset = EXYNOS_MBOX_INTMR0,
>> + .irq_mask_value = EXYNOS_MBOX_INTMR0_MASK,
>> };
>
> I find it strange that the SoCs use different registers. Are you sure you're
> using the right direction? i.e. ring the doorbell to APM and not to AP?

Well, I am not sure I correctly understood the questions and comment. So,
this all was tested with ACPM TMU code with 3 temp sensors and it seems
to work and sensors react in the right way.

Downstream clearly does the following (see also [1],[2]) when sending
ACPM msg:

static void apm_interrupt_gen(unsigned int id)
{
/* APM NVIC INTERRUPT GENERATE */
writel((1 << id) << 16, acpm_ipc->intr + INTGR0);
}

I am aware that gs101 downstream uses INTGR1 in apm_interrupt_gen().

When I use INTGR1 for e850 then I observe acpm timeouts. Hence, out of
curiosity, what's the expected behaviour when/if I ring the doorbell to
AP (to itself as far as I understand)? My understanding that it won't
work at all in such case unless APM firmware does some very fast
polling.

[1]: https://gitlab.com/Linaro/96boards/e850-96/kernel/-/blob/android-exynos-4.14-linaro/drivers/soc/samsung/acpm/acpm_ipc.c?ref_type=heads#L423
[2]: https://github.com/samsungexynos850/android_kernel_samsung_exynos850/blob/0af517be2336bf8e09c59d576c4c314446713101/drivers/soc/samsung/acpm/acpm_ipc.c#L426

>> static int exynos_mbox_send_data(struct mbox_chan *chan, void *data)
>> @@ -57,7 +104,8 @@ static int exynos_mbox_send_data(struct mbox_chan *chan, void *data)
>> return -EINVAL;
>> }
>>
>> - writel(BIT(msg->chan_id), exynos_mbox->regs + EXYNOS_MBOX_INTGR1);
>> + writel(BIT(msg->chan_id) << exynos_mbox->data->irq_doorbell_shift,
>> + exynos_mbox->regs + exynos_mbox->data->irq_doorbell_offset);
>
> Use FIELD_PREP from <linux/bitfield.h> please. You will use a mask instead of
> a shift.
>
> I would rename irq_doorbell_offset to intgr. It aligns with the register name
> from the datasheet. You won't need to prepend _offset to the name, we already
> see it's an offset when doing the writel().

Sure. Thanks. Let's use FIELD_PREP.

"doorbell" naming was chosen for readability and maintainability reasons.
It seems to be more generic enough name that better reflects the workflow
of what's going on in ACPM+mailbox machinery. We can rename it to just
"doorbell" for instance.

From platform data it will be clear to which register it is set, INTGR0
or INTGR1, to align it with datasheet (which is closed anyway).

Regarding intgr vs doorbell name, the intgr is a bit unclear for a
reader if it means interrupt generation register or something else.
But if you prefer, I can go with "intgr".

One more option is add a comment, smth like /* Ring the doorbell */
before that writel().

[..]

>> @@ -133,7 +194,7 @@ static int exynos_mbox_probe(struct platform_device *pdev)
>> platform_set_drvdata(pdev, exynos_mbox);
>>
>> /* Mask out all interrupts. We support just polling channels for now. */
>> - writel(EXYNOS_MBOX_INTMR0_MASK, exynos_mbox->regs + EXYNOS_MBOX_INTMR0);
>> + writel(data->irq_mask_value, exynos_mbox->regs + data->irq_mask_offset);
>>
>
> and here I would s/irq_mask_value/intmr_mask and irq_mask_offset/intmr.

Ack. Thanks.

Best regards,
Alexey