kernel/0006-feat-eswin-mailbox-Added-eswin-mailbox-related-chang.patch

611 lines
16 KiB
Diff
Raw Normal View History

2024-12-27 22:35:16 +00:00
From a5843416bfc021f36ab5468eefac2ec0eec099e4 Mon Sep 17 00:00:00 2001
2024-12-15 18:29:23 +00:00
From: =?UTF-8?q?=E2=80=9Chuangyifeng=E2=80=9D?=
<huangyifeng@eswincomputing.com>
Date: Wed, 17 Apr 2024 13:48:05 +0800
2024-12-27 22:35:16 +00:00
Subject: [PATCH 006/222] feat(eswin mailbox):Added eswin mailbox related
2024-12-15 18:29:23 +00:00
changes
Changelogs:
1.Added eswin mailbox dts
2.Added eswin mailbox driver
3.Select CONFIG_ESWIN_MBOX, and update win2030_defconfig
---
arch/riscv/configs/win2030_defconfig | 1 +
drivers/mailbox/Kconfig | 9 +
drivers/mailbox/Makefile | 2 +
drivers/mailbox/eswin-mailbox.c | 518 ++++++++++++++++++++++++++
include/linux/mailbox/eswin-mailbox.h | 15 +
5 files changed, 545 insertions(+)
create mode 100755 drivers/mailbox/eswin-mailbox.c
create mode 100755 include/linux/mailbox/eswin-mailbox.h
diff --git a/arch/riscv/configs/win2030_defconfig b/arch/riscv/configs/win2030_defconfig
index 2e2e4e419bd3..5d701e98d635 100644
--- a/arch/riscv/configs/win2030_defconfig
+++ b/arch/riscv/configs/win2030_defconfig
@@ -223,6 +223,7 @@ CONFIG_VIRTIO_MMIO=y
CONFIG_STAGING=y
CONFIG_COMMON_CLK_WIN2030=y
CONFIG_MAILBOX=y
+CONFIG_ESWIN_MBOX=y
CONFIG_ARM_SMMU_V3=y
CONFIG_RPMSG_VIRTIO=y
CONFIG_ARCH_ESWIN_EIC770X_SOC_FAMILY=y
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index bc2e265cb02d..3cc765e1ce8e 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -294,5 +294,14 @@ config QCOM_IPCC
sending interrupts to the clients. On the other hand, the driver also
acts as an interrupt controller for receiving interrupts from clients.
Say Y here if you want to build this driver.
+config ESWIN_MBOX
+ tristate "Eswin Mailbox"
+ depends on OF
+ depends on HAS_IOMEM
+ depends on ARCH_ESWIN_EIC770X_SOC_FAMILY
+ help
+ Mailbox driver implementation for the eswin platform. It is used
+ to send message between application processors and MCU. Say Y here if
+ you want to build the eswin mailbox controller driver.
endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index fc9376117111..157518a5d716 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -62,3 +62,5 @@ obj-$(CONFIG_SPRD_MBOX) += sprd-mailbox.o
obj-$(CONFIG_QCOM_IPCC) += qcom-ipcc.o
obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o
+
+obj-$(CONFIG_ESWIN_MBOX) += eswin-mailbox.o
diff --git a/drivers/mailbox/eswin-mailbox.c b/drivers/mailbox/eswin-mailbox.c
new file mode 100755
index 000000000000..4e9b6b224469
--- /dev/null
+++ b/drivers/mailbox/eswin-mailbox.c
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ESWIN Mailbox Driver
+ *
+ * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved.
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * Authors: HuangYiFeng<huangyifeng@eswincomputing.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/mailbox/eswin-mailbox.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define ESWIN_MBOX_FIFO_DEPTH 8
+
+struct eswin_mbox_data {
+ int num_chans;
+};
+
+struct eswin_mbox_chan {
+ int idx;
+ int irq;
+ struct eswin_mbox_msg msg[ESWIN_MBOX_FIFO_DEPTH];
+ int msg_cnt;
+ struct eswin_mbox *mb;
+};
+
+/*
+ * Registers offset
+ */
+#define ESWIN_MBOX_WR_DATA0 0x00
+#define ESWIN_MBOX_WR_DATA1 0x04
+#define ESWIN_MBOX_RD_DATA0 0x08
+#define ESWIN_MBOX_RD_DATA1 0x0C
+#define ESWIN_MBOX_FIFO_STATUS 0x10
+#define ESWIN_MBOX_MB_ERR 0x14
+#define ESWIN_MBOX_INT_CTRL 0x18
+#define ESWIN_MBOX_WR_LOCK 0x1C
+
+struct eswin_mbox {
+ struct mbox_controller mbox;
+ struct clk *pclk;
+ struct clk *pclk_device;
+ struct reset_control *rst;
+ struct reset_control *rst_device;
+ void __iomem *mbox_base;
+ void __iomem *mbox_rx_base;
+ struct regmap *map;
+ struct regmap *rx_map;
+ struct device *dev;
+ u32 lock_bit;
+ u32 irq_bit;
+ struct eswin_mbox_chan *chans;
+ spinlock_t rx_lock;
+};
+
+static int eswin_mbox_send_data(struct mbox_chan *chan, void *data)
+{
+ u32 tmp_data;
+
+ struct eswin_mbox *mb = dev_get_drvdata(chan->mbox->dev);
+ struct eswin_mbox_msg *msg = (struct eswin_mbox_msg *)data;
+
+ dev_dbg(mb->mbox.dev, "send_data\n");
+ if (!msg)
+ return -EINVAL;
+
+ // TX FIFO FULL?
+ if (regmap_test_bits(mb->map, ESWIN_MBOX_FIFO_STATUS, BIT_ULL(0))) {
+ return -EBUSY;
+ }
+
+ tmp_data = (u32)msg->data;
+ regmap_write(mb->map, ESWIN_MBOX_WR_DATA0, tmp_data);
+
+ tmp_data = (u32)(msg->data >> 32) | BIT(31);
+ regmap_write(mb->map, ESWIN_MBOX_WR_DATA1, tmp_data);
+ // 写中断enable bit.
+ regmap_set_bits(mb->map, ESWIN_MBOX_INT_CTRL, mb->irq_bit);
+ return 0;
+}
+
+static int eswin_mbox_startup(struct mbox_chan *chan)
+{
+ struct eswin_mbox *mb = dev_get_drvdata(chan->mbox->dev);
+ int ret;
+
+ if (regmap_test_bits(mb->map, ESWIN_MBOX_WR_LOCK, mb->lock_bit)) {
+ return -1;
+ }
+ ret = regmap_set_bits(mb->map, ESWIN_MBOX_WR_LOCK, mb->lock_bit);
+
+ /*占用标志位写入成功表示占用成功*/
+ dev_dbg(mb->mbox.dev, "start, ret %d, lock_bit 0x%x\n", ret,
+ mb->lock_bit);
+ return ret;
+}
+
+static void eswin_mbox_shutdown(struct mbox_chan *chan)
+{
+ struct eswin_mbox *mb = dev_get_drvdata(chan->mbox->dev);
+ int ret;
+
+ ret = regmap_clear_bits(mb->map, ESWIN_MBOX_WR_LOCK, mb->lock_bit);
+ if (0 != ret)
+ dev_err(mb->mbox.dev, "failed to shutdown mailbox\n");
+
+ ret = regmap_clear_bits(mb->map, ESWIN_MBOX_INT_CTRL, mb->irq_bit);
+ if (0 != ret)
+ dev_err(mb->mbox.dev, "failed to disable mailbox int\n");
+
+ return;
+}
+
+static int eswin_mbox_receive_data(struct eswin_mbox *mb,
+ struct eswin_mbox_msg *msg)
+{
+ u32 tmp_data;
+ u32 tmp_data0;
+ u64 tmp;
+
+ regmap_read(mb->rx_map, ESWIN_MBOX_RD_DATA0, &tmp_data0);
+ regmap_read(mb->rx_map, ESWIN_MBOX_RD_DATA1, &tmp_data);
+
+ // RX FIFO empty ?
+ if (tmp_data == 0) {
+ return -1;
+ }
+ tmp = (u64)tmp_data << 32 | tmp_data0;
+
+ msg->data = tmp;
+
+ /*trigger FIFO dequeue*/
+ regmap_write(mb->rx_map, ESWIN_MBOX_RD_DATA1, 0x0);
+ return 0;
+}
+
+static bool eswin_mbox_peek_data(struct mbox_chan *chan)
+{
+ int idx;
+ struct mbox_controller *mbox = chan->mbox;
+ struct eswin_mbox_msg *msg = NULL;
+ struct eswin_mbox *mb = container_of(mbox, struct eswin_mbox, mbox);
+ bool IsData = false;
+ unsigned long flags;
+
+ for (idx = 0; idx < mb->mbox.num_chans; idx++) {
+ spin_lock_irqsave(&mb->rx_lock, flags);
+ msg = &mb->chans[idx].msg[0];
+ if (0 != eswin_mbox_receive_data(mb, msg)) {
+ spin_unlock_irqrestore(&mb->rx_lock, flags);
+ continue;
+ }
+ IsData = true;
+ mb->chans[idx].msg_cnt--;
+ if (NULL != mb->mbox.chans[idx].cl) {
+ mbox_chan_received_data(&mb->mbox.chans[idx], msg);
+ }
+ spin_unlock_irqrestore(&mb->rx_lock, flags);
+ }
+ return IsData;
+}
+
+/*
+ once the data has been enqueued to mailbox hw FIFO in send_data function,
+ we beleive that tx is done
+*/
+static bool eswin_mbox_last_tx_done(struct mbox_chan *chan)
+{
+ return true;
+}
+
+static const struct mbox_chan_ops eswin_mbox_chan_ops = {
+ .send_data = eswin_mbox_send_data,
+ .peek_data = eswin_mbox_peek_data,
+ .startup = eswin_mbox_startup,
+ .shutdown = eswin_mbox_shutdown,
+ .last_tx_done = eswin_mbox_last_tx_done,
+};
+
+static irqreturn_t eswin_mbox_irq(int irq, void *dev_id)
+{
+ int idx;
+ struct eswin_mbox *mb = (struct eswin_mbox *)dev_id;
+ unsigned long flags;
+
+ for (idx = 0; idx < mb->mbox.num_chans; idx++) {
+ if (irq != mb->chans[idx].irq)
+ continue;
+
+ spin_lock_irqsave(&mb->rx_lock, flags);
+ WARN_ON(0 != mb->chans[idx].msg_cnt);
+ while (0 ==
+ eswin_mbox_receive_data(
+ mb,
+ &mb->chans[idx].msg[mb->chans[idx].msg_cnt])) {
+ mb->chans[idx].msg_cnt++;
+ /*
+ MCU may continue enqeuing fifo when we are dequeuing fifo,
+ So we receive up to ESWIN_MBOX_FIFO_DEPTH cnt msgs one time.
+ The left msgs will be handled after eswin_mbox_isr finished.
+ */
+ if (ESWIN_MBOX_FIFO_DEPTH == mb->chans[idx].msg_cnt) {
+ break;
+ }
+ };
+ spin_unlock_irqrestore(&mb->rx_lock, flags);
+ }
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t eswin_mbox_isr(int irq, void *dev_id)
+{
+ int idx;
+ struct eswin_mbox_msg *msg = NULL;
+ struct eswin_mbox *mb = (struct eswin_mbox *)dev_id;
+ unsigned long flags;
+ int i;
+
+ for (idx = 0; idx < mb->mbox.num_chans; idx++) {
+ if (irq != mb->chans[idx].irq)
+ continue;
+
+ i = 0;
+ spin_lock_irqsave(&mb->rx_lock, flags);
+ while (mb->chans[idx].msg_cnt) {
+ msg = &mb->chans[idx].msg[i++];
+ if (NULL != mb->mbox.chans[idx].cl) {
+ dev_dbg(mb->mbox.dev,
+ "Chan[%d]: receive MCU message, msg %p\n",
+ idx, msg);
+ mbox_chan_received_data(&mb->mbox.chans[idx],
+ msg);
+ }
+ mb->chans[idx].msg_cnt--;
+ }
+ spin_unlock_irqrestore(&mb->rx_lock, flags);
+ }
+ return IRQ_HANDLED;
+}
+
+static const struct eswin_mbox_data win2030_drv_data = {
+ .num_chans = 1,
+};
+
+static const struct of_device_id eswin_mbox_of_match[] = {
+ { .compatible = "eswin,win2030-mailbox", .data = &win2030_drv_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, eswin_mbox_of_match);
+
+static int eswin_mbox_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct eswin_mbox *mb = context;
+
+ *val = readl_relaxed(mb->mbox_base + reg);
+ return 0;
+}
+
+static int eswin_mbox_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct eswin_mbox *mb = context;
+
+ writel_relaxed(val, mb->mbox_base + reg);
+ return 0;
+}
+
+static int eswin_mbox_rx_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct eswin_mbox *mb = context;
+
+ *val = readl_relaxed(mb->mbox_rx_base + reg);
+ return 0;
+}
+
+static int eswin_mbox__rx_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct eswin_mbox *mb = context;
+
+ writel_relaxed(val, mb->mbox_rx_base + reg);
+ return 0;
+}
+
+/**
+ * eswin_mbox_init_regmap() - Initialize registers map
+ * @dev: device private data
+ *
+ * Autodetects needed register access mode and creates the regmap with
+ * corresponding read/write callbacks. This must be called before doing any
+ * other register access.
+ */
+int eswin_mbox_init_regmap(struct eswin_mbox *mb)
+{
+ struct regmap_config map_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .use_hwlock = true,
+ .cache_type = REGCACHE_NONE,
+ .can_sleep = false,
+ .reg_read = eswin_mbox_reg_read,
+ .reg_write = eswin_mbox_reg_write,
+ };
+ struct regmap_config rx_map_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .use_hwlock = true,
+ .cache_type = REGCACHE_NONE,
+ .can_sleep = false,
+ .reg_read = eswin_mbox_rx_reg_read,
+ .reg_write = eswin_mbox__rx_reg_write,
+ };
+
+ /*
+ * Note we'll check the return value of the regmap IO accessors only
+ * at the probe stage. The rest of the code won't do this because
+ * basically we have MMIO-based regmap so non of the read/write methods
+ * can fail.
+ */
+ mb->map = devm_regmap_init(mb->dev, NULL, mb, &map_cfg);
+ if (IS_ERR(mb->map)) {
+ dev_err(mb->dev, "Failed to init the registers map\n");
+ return PTR_ERR(mb->map);
+ }
+ mb->rx_map = devm_regmap_init(mb->dev, NULL, mb, &rx_map_cfg);
+ if (IS_ERR(mb->rx_map)) {
+ dev_err(mb->dev, "Failed to init the registers rx map\n");
+ regmap_exit(mb->map);
+ return PTR_ERR(mb->rx_map);
+ }
+
+ return 0;
+}
+
+static int eswin_mbox_probe(struct platform_device *pdev)
+{
+ struct eswin_mbox *mb;
+ const struct of_device_id *match;
+ const struct eswin_mbox_data *drv_data;
+ struct resource *res;
+ int ret, irq, i;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ match = of_match_node(eswin_mbox_of_match, pdev->dev.of_node);
+ drv_data = (const struct eswin_mbox_data *)match->data;
+
+ mb = devm_kzalloc(&pdev->dev, sizeof(*mb), GFP_KERNEL);
+ if (!mb)
+ return -ENOMEM;
+
+ if (of_property_read_u32(pdev->dev.of_node, "lock-bit",
+ &mb->lock_bit)) {
+ dev_err(&pdev->dev, "failed to get lock_bit: %d\n", ret);
+ }
+
+ if (of_property_read_u32(pdev->dev.of_node, "irq-bit", &mb->irq_bit)) {
+ dev_err(&pdev->dev, "failed to get irq_bit: %d\n", ret);
+ }
+ mb->chans = devm_kcalloc(&pdev->dev, drv_data->num_chans,
+ sizeof(*mb->chans), GFP_KERNEL);
+ if (!mb->chans)
+ return -ENOMEM;
+
+ mb->mbox.chans = devm_kcalloc(&pdev->dev, drv_data->num_chans,
+ sizeof(*mb->mbox.chans), GFP_KERNEL);
+ if (!mb->mbox.chans)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mb);
+
+ mb->mbox.dev = &pdev->dev;
+ mb->mbox.num_chans = drv_data->num_chans;
+ mb->mbox.ops = &eswin_mbox_chan_ops;
+ mb->mbox.txdone_irq = false;
+ mb->mbox.txdone_poll = true;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ mb->mbox_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mb->mbox_base))
+ return PTR_ERR(mb->mbox_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res)
+ return -ENODEV;
+
+ mb->mbox_rx_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mb->mbox_rx_base))
+ return PTR_ERR(mb->mbox_rx_base);
+
+ mb->pclk = devm_clk_get(&pdev->dev, "pclk_mailbox_host");
+ if (IS_ERR(mb->pclk)) {
+ ret = PTR_ERR(mb->pclk);
+ dev_err(&pdev->dev, "failed to get host mailbox clock: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(mb->pclk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable host mailbox pclk: %d\n",
+ ret);
+ return ret;
+ }
+
+ mb->pclk_device = devm_clk_get(&pdev->dev, "pclk_mailbox_device");
+ if (IS_ERR(mb->pclk_device)) {
+ ret = PTR_ERR(mb->pclk_device);
+ dev_err(&pdev->dev, "failed to get device mailbox clock: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(mb->pclk_device);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to enable device mailbox pclk: %d\n", ret);
+ return ret;
+ }
+
+ mb->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, "rst");
+ if (IS_ERR(mb->rst))
+ return PTR_ERR(mb->rst);
+ reset_control_reset(mb->rst);
+
+ mb->rst_device = devm_reset_control_get_optional_exclusive(
+ &pdev->dev, "rst_device");
+ if (IS_ERR(mb->rst_device))
+ return PTR_ERR(mb->rst_device);
+ reset_control_reset(mb->rst_device);
+
+ for (i = 0; i < mb->mbox.num_chans; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, eswin_mbox_irq,
+ eswin_mbox_isr, IRQF_ONESHOT,
+ dev_name(&pdev->dev), mb);
+ if (ret < 0)
+ return ret;
+
+ mb->chans[i].idx = i;
+ mb->chans[i].irq = irq;
+ mb->chans[i].mb = mb;
+ }
+ mb->dev = &pdev->dev;
+ ret = eswin_mbox_init_regmap(mb);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&mb->rx_lock);
+ ret = devm_mbox_controller_register(&pdev->dev, &mb->mbox);
+ if (ret < 0)
+ dev_err(&pdev->dev, "failed to register mailbox: %d\n", ret);
+
+ dev_info(&pdev->dev, "register sucessfully\n");
+ return ret;
+}
+
+static int eswin_mbox_remove(struct platform_device *pdev)
+{
+ int ret;
+ struct eswin_mbox *mb = platform_get_drvdata(pdev);
+
+ ret = reset_control_assert(mb->rst);
+ WARN_ON(ret != 0);
+ ret = reset_control_assert(mb->rst_device);
+ WARN_ON(ret != 0);
+ clk_disable_unprepare(mb->pclk_device);
+ clk_disable_unprepare(mb->pclk);
+ return 0;
+}
+
+static struct platform_driver eswin_mbox_driver = {
+ .probe = eswin_mbox_probe,
+ .remove = eswin_mbox_remove,
+ .driver = {
+ .name = "eswin-mailbox",
+ .of_match_table = of_match_ptr(eswin_mbox_of_match),
+ },
+};
+
+module_platform_driver(eswin_mbox_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Eswin mailbox: communicate between CPU cores and MCUs");
+MODULE_AUTHOR("Huang Yifeng <huangyifeng@eswincomputing.com>");
diff --git a/include/linux/mailbox/eswin-mailbox.h b/include/linux/mailbox/eswin-mailbox.h
new file mode 100755
index 000000000000..6389befb5b90
--- /dev/null
+++ b/include/linux/mailbox/eswin-mailbox.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _LINUX_ESWIN_MAILBOX_H_
+#define _LINUX_ESWIN_MAILBOX_H_
+
+/**
+ * struct eswin_mbox_msg - Eswin mailbox message structure
+ * @data: message payload, only 63 bit valid
+ *
+ */
+struct eswin_mbox_msg {
+ u64 data;
+};
+
+#endif /* _LINUX_ESWIN_MAILBOX_H_ */
--
2.47.0