mailbox: add PolarFire SoC mailbox driver
authorJamie Gibbons <jamie.gibbons@microchip.com>
Fri, 1 Aug 2025 12:36:23 +0000 (13:36 +0100)
committerLeo Yu-Chi Liang <ycliang@andestech.com>
Thu, 14 Aug 2025 07:33:00 +0000 (15:33 +0800)
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>
drivers/mailbox/Kconfig
drivers/mailbox/Makefile
drivers/mailbox/mpfs-mbox.c [new file with mode: 0644]
include/mpfs-mailbox.h [new file with mode: 0644]

index 4d9f004..f9531c1 100644 (file)
@@ -28,6 +28,13 @@ config IMX_MU_MBOX
          Enable support for i.MX Messaging Unit for communication with other
          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
        bool "Enable the sandbox mailbox test driver"
        depends on DM_MAILBOX && SANDBOX
index e8c745f..b54fbdf 100644 (file)
@@ -6,6 +6,7 @@
 obj-$(CONFIG_$(PHASE_)DM_MAILBOX) += mailbox-uclass.o
 obj-$(CONFIG_APPLE_MBOX) += apple-mbox.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-test.o
 obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o
diff --git a/drivers/mailbox/mpfs-mbox.c b/drivers/mailbox/mpfs-mbox.c
new file mode 100644 (file)
index 0000000..5523884
--- /dev/null
@@ -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,
+};
diff --git a/include/mpfs-mailbox.h b/include/mpfs-mailbox.h
new file mode 100644 (file)
index 0000000..c0ff327
--- /dev/null
@@ -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__ */
+