mailbox: add PolarFire SoC mailbox driver

This driver adds support for the single mailbox channel of the MSS
system controller on the Microchip PolarFire SoC.

Signed-off-by: Jamie Gibbons <jamie.gibbons@microchip.com>
Acked-by: Leo Yu-Chi Liang <ycliang@andestech.com>
This commit is contained in:
Jamie Gibbons
2025-08-01 13:36:23 +01:00
committed by Leo Yu-Chi Liang
parent 69539ef2f1
commit 111e9bf6a5
4 changed files with 251 additions and 0 deletions

View File

@@ -28,6 +28,13 @@ config IMX_MU_MBOX
Enable support for i.MX Messaging Unit for communication with other Enable support for i.MX Messaging Unit for communication with other
processors on the SoC using mailbox interface processors on the SoC using mailbox interface
config MPFS_MBOX
bool "Enable MPFS system controller support"
depends on DM_MAILBOX && ARCH_RV64I
help
Enable support for the mailboxes that provide a communication
channel with the system controller integrated on PolarFire SoC.
config SANDBOX_MBOX config SANDBOX_MBOX
bool "Enable the sandbox mailbox test driver" bool "Enable the sandbox mailbox test driver"
depends on DM_MAILBOX && SANDBOX depends on DM_MAILBOX && SANDBOX

View File

@@ -6,6 +6,7 @@
obj-$(CONFIG_$(PHASE_)DM_MAILBOX) += mailbox-uclass.o obj-$(CONFIG_$(PHASE_)DM_MAILBOX) += mailbox-uclass.o
obj-$(CONFIG_APPLE_MBOX) += apple-mbox.o obj-$(CONFIG_APPLE_MBOX) += apple-mbox.o
obj-$(CONFIG_IMX_MU_MBOX) += imx-mailbox.o obj-$(CONFIG_IMX_MU_MBOX) += imx-mailbox.o
obj-$(CONFIG_MPFS_MBOX) += mpfs-mbox.o
obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o
obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o
obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o

177
drivers/mailbox/mpfs-mbox.c Normal file
View File

@@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Microchip's PolarFire SoC (MPFS) Mailbox Driver
*
* Copyright (C) 2024 Microchip Technology Inc. All rights reserved.
*
* Author: Jamie Gibbons <jamie.gibbons@microchip.com>
*
*/
#include <asm/io.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/device.h>
#include <dm/device_compat.h>
#include <dm/devres.h>
#include <dm/ofnode.h>
#include <linux/bitops.h>
#include <linux/compat.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <log.h>
#include <mailbox-uclass.h>
#include <malloc.h>
#include <mpfs-mailbox.h>
#define SERVICES_CR_OFFSET 0x50u
#define SERVICES_SR_OFFSET 0x54u
#define SERVICE_CR_REQ_MASK 0x1u
#define SERVICE_SR_BUSY_MASK 0x2u
#define SERVICE_SR_STATUS_SHIFT 16
#define SERVICE_CR_COMMAND_SHIFT 16
#define MASK_8BIT 0xFF
struct mpfs_mbox {
struct udevice *dev;
void __iomem *ctrl_base;
void __iomem *mbox_base;
struct mbox_chan *chan;
};
static bool mpfs_mbox_busy(struct mbox_chan *chan)
{
struct mpfs_mbox *mbox = dev_get_priv(chan->dev);
uint16_t status;
status = readl(mbox->ctrl_base + SERVICES_SR_OFFSET);
return status & SERVICE_SR_BUSY_MASK;
}
static int mpfs_mbox_send(struct mbox_chan *chan, const void *data)
{
struct mpfs_mbox *mbox = dev_get_priv(chan->dev);
struct mpfs_mss_msg *msg = (struct mpfs_mss_msg *)data;
u32 mailbox_val, cmd_shifted, value;
u8 *byte_buf;
u8 idx, byte_idx, byte_offset;
u32 *word_buf = (u32 *)msg->cmd_data;
if (mpfs_mbox_busy(chan))
return -EBUSY;
for (idx = 0; idx < (msg->cmd_data_size / BYTES_4); idx++)
writel(word_buf[idx], mbox->mbox_base + msg->mbox_offset + idx * BYTES_4);
if ((msg->cmd_data_size % BYTES_4) > 0) {
byte_offset = (msg->cmd_data_size / BYTES_4) * BYTES_4;
byte_buf = (u8 *)(msg->cmd_data + byte_offset);
mailbox_val = readl(mbox->mbox_base + msg->mbox_offset + idx * BYTES_4);
for (byte_idx = 0; byte_idx < (msg->cmd_data_size % BYTES_4); byte_idx++) {
mailbox_val &= ~(MASK_8BIT << (byte_idx * 0x8u));
mailbox_val |= (u32)byte_buf[byte_idx] << (byte_idx * 0x8u);
}
writel(mailbox_val, mbox->mbox_base + msg->mbox_offset + idx * BYTES_4);
}
cmd_shifted = msg->cmd_opcode << SERVICE_CR_COMMAND_SHIFT;
cmd_shifted |= SERVICE_CR_REQ_MASK;
writel(cmd_shifted, mbox->ctrl_base + SERVICES_CR_OFFSET);
do {
value = readl(mbox->ctrl_base + SERVICES_CR_OFFSET);
} while (SERVICE_CR_REQ_MASK == (value & SERVICE_CR_REQ_MASK));
do {
value = readl(mbox->ctrl_base + SERVICES_SR_OFFSET);
} while (SERVICE_SR_BUSY_MASK == (value & SERVICE_SR_BUSY_MASK));
msg->response->resp_status = (value >> SERVICE_SR_STATUS_SHIFT);
if (msg->response->resp_status)
return -EBADMSG;
return 0;
}
static int mpfs_mbox_recv(struct mbox_chan *chan, void *data)
{
struct mpfs_mbox *mbox = dev_get_priv(chan->dev);
struct mpfs_mss_msg *msg = data;
struct mpfs_mss_response *response = msg->response;
u8 idx;
if (!response->resp_msg) {
dev_err(chan->dev, "failed to assign memory for response %d\n", -ENOMEM);
return -EINVAL;
}
if (mpfs_mbox_busy(chan)) {
dev_err(chan->dev, "mailbox is busy\n");
response->resp_status = 0xDEAD;
return -EINVAL;
}
for (idx = 0; idx < response->resp_size; idx++)
*((u8 *)(response->resp_msg) + idx) = readb(mbox->mbox_base + msg->resp_offset + idx);
return 0;
}
static const struct mbox_ops mpfs_mbox_ops = {
.send = mpfs_mbox_send,
.recv = mpfs_mbox_recv,
};
static int mpfs_mbox_probe(struct udevice *dev)
{
struct mpfs_mbox *mbox;
struct resource regs;
ofnode node;
int ret;
node = dev_ofnode(dev);
mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
if (!mbox)
return -ENOMEM;
ret = ofnode_read_resource(node, 0, &regs);
if (ret) {
dev_err(dev, "No reg property for controller base\n");
return ret;
};
mbox->ctrl_base = devm_ioremap(dev, regs.start, regs.start - regs.end);
ret = ofnode_read_resource(node, 2, &regs);
if (ret) {
dev_err(dev, "No reg property for mailbox base\n");
return ret;
};
mbox->mbox_base = devm_ioremap(dev, regs.start, regs.start - regs.end);
mbox->dev = dev;
dev_set_priv(dev, mbox);
mbox->chan->con_priv = mbox;
return 0;
}
static const struct udevice_id mpfs_mbox_ids[] = {
{.compatible = "microchip,mpfs-mailbox"},
{ }
};
U_BOOT_DRIVER(mpfs_mbox) = {
.name = "mpfs-mbox",
.id = UCLASS_MAILBOX,
.of_match = mpfs_mbox_ids,
.probe = mpfs_mbox_probe,
.priv_auto = sizeof(struct mpfs_mbox),
.ops = &mpfs_mbox_ops,
};

66
include/mpfs-mailbox.h Normal file
View File

@@ -0,0 +1,66 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
*
* Microchip PolarFire SoC (MPFS)
*
* Copyright (c) 2020 Microchip Corporation. All rights reserved.
*
*
*/
#ifndef _MPFS_MAILBOX_H__
#define _MPFS_MAILBOX_H__
#include <linux/types.h>
#define BYTES_4 4
struct udevice;
/**
* struct mpfs_mss_msg - PolarFire SoC message structure
* @cmd_opcode: Command opcode
* @cmd_data_size: Size of the command data.
* @response: Pointer to the response data.
* @cmd_data: Pointer to the command data.
* @mbox_offset: Mailbox offset
* @resp_offset: Response offset
*
*/
struct mpfs_mss_msg {
u8 cmd_opcode;
u16 cmd_data_size;
struct mpfs_mss_response *response;
u8 *cmd_data;
u16 mbox_offset;
u16 resp_offset;
};
/**
* struct mpfs_mss_response - PolarFire SoC response structure
* @resp_status: Response status
* @resp_msg: Pointer to response message.
* @resp_size: Size of the response message.
*
*/
struct mpfs_mss_response {
u32 resp_status;
u32 *resp_msg;
u16 resp_size;
};
struct mpfs_syscontroller_priv;
struct mpfs_sys_serv {
struct udevice *dev;
struct mpfs_syscontroller_priv *sys_controller;
struct mpfs_mss_msg *msg;
};
int mpfs_syscontroller_run_service(struct mpfs_syscontroller_priv *sys_controller, struct mpfs_mss_msg *msg);
int mpfs_syscontroller_read_sernum(struct mpfs_sys_serv *sys_serv_priv, u8 *device_serial_number);
void mpfs_syscontroller_process_dtbo(struct mpfs_sys_serv *sys_serv_priv);
struct mpfs_syscontroller_priv *mpfs_syscontroller_get(struct udevice *dev);
#endif /* __MPFS_MAILBOX_H__ */