diff options
Diffstat (limited to 'meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.10/tc/0004-mailbox-arm_mhuv2-Add-driver.patch')
-rw-r--r-- | meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.10/tc/0004-mailbox-arm_mhuv2-Add-driver.patch | 1256 |
1 files changed, 0 insertions, 1256 deletions
diff --git a/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.10/tc/0004-mailbox-arm_mhuv2-Add-driver.patch b/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.10/tc/0004-mailbox-arm_mhuv2-Add-driver.patch deleted file mode 100644 index a4dd9612a1..0000000000 --- a/meta-arm/meta-arm-bsp/recipes-kernel/linux/linux-arm64-ack-5.10/tc/0004-mailbox-arm_mhuv2-Add-driver.patch +++ /dev/null @@ -1,1256 +0,0 @@ -From eadd9235d084da8022df2d232c90590f2160e433 Mon Sep 17 00:00:00 2001 -From: Viresh Kumar <viresh.kumar@linaro.org> -Date: Tue, 17 Nov 2020 15:32:06 +0530 -Subject: [PATCH 04/22] mailbox: arm_mhuv2: Add driver - -This adds driver for the ARM MHUv2 (Message Handling Unit) mailbox -controller. - -This is based on the accepted DT bindings of the controller and supports -combination of both transport protocols, i.e. doorbell and data-transfer. - -Transmitting and receiving data through the mailbox framework is done -through struct arm_mhuv2_mbox_msg. - -Based on the initial work done by Morten Borup Petersen from ARM. - -Co-developed-by: Tushar Khandelwal <tushar.khandelwal@arm.com> -Signed-off-by: Tushar Khandelwal <tushar.khandelwal@arm.com> -Tested-by: Usama Arif <usama.arif@arm.com> -Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> -Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org> - -Upstream-Status: Backport [https://www.lkml.org/lkml/2020/11/17/235] -Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com> ---- - MAINTAINERS | 9 + - drivers/mailbox/Kconfig | 7 + - drivers/mailbox/Makefile | 2 + - drivers/mailbox/arm_mhuv2.c | 1136 +++++++++++++++++++++ - include/linux/mailbox/arm_mhuv2_message.h | 20 + - 5 files changed, 1174 insertions(+) - create mode 100644 drivers/mailbox/arm_mhuv2.c - create mode 100644 include/linux/mailbox/arm_mhuv2_message.h - -diff --git a/MAINTAINERS b/MAINTAINERS -index 354831907474..5234423c477a 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -10459,6 +10459,15 @@ F: drivers/mailbox/ - F: include/linux/mailbox_client.h - F: include/linux/mailbox_controller.h - -+MAILBOX ARM MHUv2 -+M: Viresh Kumar <viresh.kumar@linaro.org> -+M: Tushar Khandelwal <Tushar.Khandelwal@arm.com> -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/mailbox/arm_mhuv2.c -+F: include/linux/mailbox/arm_mhuv2_message.h -+F: Documentation/devicetree/bindings/mailbox/arm,mhuv2.yaml -+ - MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7 - M: Michael Kerrisk <mtk.manpages@gmail.com> - L: linux-man@vger.kernel.org -diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig -index 05b1009e2820..3c0ea96a0a8b 100644 ---- a/drivers/mailbox/Kconfig -+++ b/drivers/mailbox/Kconfig -@@ -16,6 +16,13 @@ config ARM_MHU - The controller has 3 mailbox channels, the last of which can be - used in Secure mode only. - -+config ARM_MHU_V2 -+ tristate "ARM MHUv2 Mailbox" -+ depends on ARM_AMBA -+ help -+ Say Y here if you want to build the ARM MHUv2 controller driver, -+ which provides unidirectional mailboxes between processing elements. -+ - config IMX_MBOX - tristate "i.MX Mailbox" - depends on ARCH_MXC || COMPILE_TEST -diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile -index 2e06e02b2e03..7194fa92c787 100644 ---- a/drivers/mailbox/Makefile -+++ b/drivers/mailbox/Makefile -@@ -7,6 +7,8 @@ obj-$(CONFIG_MAILBOX_TEST) += mailbox-test.o - - obj-$(CONFIG_ARM_MHU) += arm_mhu.o arm_mhu_db.o - -+obj-$(CONFIG_ARM_MHU_V2) += arm_mhuv2.o -+ - obj-$(CONFIG_IMX_MBOX) += imx-mailbox.o - - obj-$(CONFIG_ARMADA_37XX_RWTM_MBOX) += armada-37xx-rwtm-mailbox.o -diff --git a/drivers/mailbox/arm_mhuv2.c b/drivers/mailbox/arm_mhuv2.c -new file mode 100644 -index 000000000000..67fb10885bb4 ---- /dev/null -+++ b/drivers/mailbox/arm_mhuv2.c -@@ -0,0 +1,1136 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * ARM Message Handling Unit Version 2 (MHUv2) driver. -+ * -+ * Copyright (C) 2020 ARM Ltd. -+ * Copyright (C) 2020 Linaro Ltd. -+ * -+ * An MHUv2 mailbox controller can provide up to 124 channel windows (each 32 -+ * bit long) and the driver allows any combination of both the transport -+ * protocol modes: data-transfer and doorbell, to be used on those channel -+ * windows. -+ * -+ * The transport protocols should be specified in the device tree entry for the -+ * device. The transport protocols determine how the underlying hardware -+ * resources of the device are utilized when transmitting data. Refer to the -+ * device tree bindings of the ARM MHUv2 controller for more details. -+ * -+ * The number of registered mailbox channels is dependent on both the underlying -+ * hardware - mainly the number of channel windows implemented by the platform, -+ * as well as the selected transport protocols. -+ * -+ * The MHUv2 controller can work both as a sender and receiver, but the driver -+ * and the DT bindings support unidirectional transfers for better allocation of -+ * the channels. That is, this driver will be probed for two separate devices -+ * for each mailbox controller, a sender device and a receiver device. -+ */ -+ -+#include <linux/amba/bus.h> -+#include <linux/interrupt.h> -+#include <linux/mailbox_controller.h> -+#include <linux/mailbox/arm_mhuv2_message.h> -+#include <linux/module.h> -+#include <linux/of_address.h> -+#include <linux/spinlock.h> -+ -+/* ====== MHUv2 Registers ====== */ -+ -+/* Maximum number of channel windows */ -+#define MHUV2_CH_WN_MAX 124 -+/* Number of combined interrupt status registers */ -+#define MHUV2_CMB_INT_ST_REG_CNT 4 -+#define MHUV2_STAT_BYTES (sizeof(u32)) -+#define MHUV2_STAT_BITS (MHUV2_STAT_BYTES * __CHAR_BIT__) -+ -+#define LSB_MASK(n) ((1 << (n * __CHAR_BIT__)) - 1) -+#define MHUV2_PROTOCOL_PROP "arm,mhuv2-protocols" -+ -+/* Register Message Handling Unit Configuration fields */ -+struct mhu_cfg_t { -+ u32 num_ch : 7; -+ u32 pad : 25; -+} __packed; -+ -+/* register Interrupt Status fields */ -+struct int_st_t { -+ u32 nr2r : 1; -+ u32 r2nr : 1; -+ u32 pad : 30; -+} __packed; -+ -+/* Register Interrupt Clear fields */ -+struct int_clr_t { -+ u32 nr2r : 1; -+ u32 r2nr : 1; -+ u32 pad : 30; -+} __packed; -+ -+/* Register Interrupt Enable fields */ -+struct int_en_t { -+ u32 r2nr : 1; -+ u32 nr2r : 1; -+ u32 chcomb : 1; -+ u32 pad : 29; -+} __packed; -+ -+/* Register Implementer Identification fields */ -+struct iidr_t { -+ u32 implementer : 12; -+ u32 revision : 4; -+ u32 variant : 4; -+ u32 product_id : 12; -+} __packed; -+ -+/* Register Architecture Identification Register fields */ -+struct aidr_t { -+ u32 arch_minor_rev : 4; -+ u32 arch_major_rev : 4; -+ u32 pad : 24; -+} __packed; -+ -+/* Sender Channel Window fields */ -+struct mhu2_send_ch_wn_reg { -+ u32 stat; -+ u8 pad1[0x0C - 0x04]; -+ u32 stat_set; -+ u32 int_st; -+ u32 int_clr; -+ u32 int_en; -+ u8 pad2[0x20 - 0x1C]; -+} __packed; -+ -+/* Sender frame register fields */ -+struct mhu2_send_frame_reg { -+ struct mhu2_send_ch_wn_reg ch_wn[MHUV2_CH_WN_MAX]; -+ struct mhu_cfg_t mhu_cfg; -+ u32 resp_cfg; -+ u32 access_request; -+ u32 access_ready; -+ struct int_st_t int_st; -+ struct int_clr_t int_clr; -+ struct int_en_t int_en; -+ u32 reserved0; -+ u32 chcomb_int_st[MHUV2_CMB_INT_ST_REG_CNT]; -+ u8 pad[0xFC8 - 0xFB0]; -+ struct iidr_t iidr; -+ struct aidr_t aidr; -+} __packed; -+ -+/* Receiver Channel Window fields */ -+struct mhu2_recv_ch_wn_reg { -+ u32 stat; -+ u32 stat_masked; -+ u32 stat_clear; -+ u8 reserved0[0x10 - 0x0C]; -+ u32 mask; -+ u32 mask_set; -+ u32 mask_clear; -+ u8 pad[0x20 - 0x1C]; -+} __packed; -+ -+/* Receiver frame register fields */ -+struct mhu2_recv_frame_reg { -+ struct mhu2_recv_ch_wn_reg ch_wn[MHUV2_CH_WN_MAX]; -+ struct mhu_cfg_t mhu_cfg; -+ u8 reserved0[0xF90 - 0xF84]; -+ struct int_st_t int_st; -+ struct int_clr_t int_clr; -+ struct int_en_t int_en; -+ u32 pad; -+ u32 chcomb_int_st[MHUV2_CMB_INT_ST_REG_CNT]; -+ u8 reserved2[0xFC8 - 0xFB0]; -+ struct iidr_t iidr; -+ struct aidr_t aidr; -+} __packed; -+ -+ -+/* ====== MHUv2 data structures ====== */ -+ -+enum mhuv2_transport_protocol { -+ DOORBELL = 0, -+ DATA_TRANSFER = 1 -+}; -+ -+enum mhuv2_frame { -+ RECEIVER_FRAME, -+ SENDER_FRAME -+}; -+ -+/** -+ * struct mhuv2 - MHUv2 mailbox controller data -+ * -+ * @mbox: Mailbox controller belonging to the MHU frame. -+ * @send/recv: Base address of the register mapping region. -+ * @frame: Frame type: RECEIVER_FRAME or SENDER_FRAME. -+ * @irq: Interrupt. -+ * @windows: Channel windows implemented by the platform. -+ * @minor: Minor version of the controller. -+ * @length: Length of the protocols array in bytes. -+ * @protocols: Raw protocol information, derived from device tree. -+ * @doorbell_pending_lock: spinlock required for correct operation of Tx -+ * interrupt for doorbells. -+ */ -+struct mhuv2 { -+ struct mbox_controller mbox; -+ union { -+ struct mhu2_send_frame_reg __iomem *send; -+ struct mhu2_recv_frame_reg __iomem *recv; -+ }; -+ enum mhuv2_frame frame; -+ unsigned int irq; -+ unsigned int windows; -+ unsigned int minor; -+ unsigned int length; -+ u32 *protocols; -+ -+ spinlock_t doorbell_pending_lock; -+}; -+ -+#define mhu_from_mbox(_mbox) container_of(_mbox, struct mhuv2, mbox) -+ -+/** -+ * struct mhuv2_protocol_ops - MHUv2 operations -+ * -+ * Each transport protocol must provide an implementation of the operations -+ * provided here. -+ * -+ * @rx_startup: Startup callback for receiver. -+ * @rx_shutdown: Shutdown callback for receiver. -+ * @read_data: Reads and clears newly available data. -+ * @tx_startup: Startup callback for receiver. -+ * @tx_shutdown: Shutdown callback for receiver. -+ * @last_tx_done: Report back if the last tx is completed or not. -+ * @send_data: Send data to the receiver. -+ */ -+struct mhuv2_protocol_ops { -+ int (*rx_startup)(struct mhuv2 *mhu, struct mbox_chan *chan); -+ void (*rx_shutdown)(struct mhuv2 *mhu, struct mbox_chan *chan); -+ void *(*read_data)(struct mhuv2 *mhu, struct mbox_chan *chan); -+ -+ void (*tx_startup)(struct mhuv2 *mhu, struct mbox_chan *chan); -+ void (*tx_shutdown)(struct mhuv2 *mhu, struct mbox_chan *chan); -+ int (*last_tx_done)(struct mhuv2 *mhu, struct mbox_chan *chan); -+ int (*send_data)(struct mhuv2 *mhu, struct mbox_chan *chan, void *arg); -+}; -+ -+/* -+ * MHUv2 mailbox channel's private information -+ * -+ * @ops: protocol specific ops for the channel. -+ * @ch_wn_idx: Channel window index allocated to the channel. -+ * @windows: Total number of windows consumed by the channel, only relevant -+ * in DATA_TRANSFER protocol. -+ * @doorbell: Doorbell bit number within the ch_wn_idx window, only relevant -+ * in DOORBELL protocol. -+ * @pending: Flag indicating pending doorbell interrupt, only relevant in -+ * DOORBELL protocol. -+ */ -+struct mhuv2_mbox_chan_priv { -+ const struct mhuv2_protocol_ops *ops; -+ u32 ch_wn_idx; -+ union { -+ u32 windows; -+ struct { -+ u32 doorbell; -+ u32 pending; -+ }; -+ }; -+}; -+ -+/* Macro for reading a bitfield within a physically mapped packed struct */ -+#define readl_relaxed_bitfield(_regptr, _field) \ -+ ({ \ -+ u32 _regval; \ -+ _regval = readl_relaxed((_regptr)); \ -+ (*(typeof((_regptr)))(&_regval))._field; \ -+ }) -+ -+/* Macro for writing a bitfield within a physically mapped packed struct */ -+#define writel_relaxed_bitfield(_value, _regptr, _field) \ -+ ({ \ -+ u32 _regval; \ -+ _regval = readl_relaxed(_regptr); \ -+ (*(typeof(_regptr))(&_regval))._field = _value; \ -+ writel_relaxed(_regval, _regptr); \ -+ }) -+ -+ -+/* =================== Doorbell transport protocol operations =============== */ -+ -+static int mhuv2_doorbell_rx_startup(struct mhuv2 *mhu, struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ writel_relaxed(BIT(priv->doorbell), -+ &mhu->recv->ch_wn[priv->ch_wn_idx].mask_clear); -+ return 0; -+} -+ -+static void mhuv2_doorbell_rx_shutdown(struct mhuv2 *mhu, -+ struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ writel_relaxed(BIT(priv->doorbell), -+ &mhu->recv->ch_wn[priv->ch_wn_idx].mask_set); -+} -+ -+static void *mhuv2_doorbell_read_data(struct mhuv2 *mhu, struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ writel_relaxed(BIT(priv->doorbell), -+ &mhu->recv->ch_wn[priv->ch_wn_idx].stat_clear); -+ return NULL; -+} -+ -+static int mhuv2_doorbell_last_tx_done(struct mhuv2 *mhu, -+ struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ return !(readl_relaxed(&mhu->send->ch_wn[priv->ch_wn_idx].stat) & -+ BIT(priv->doorbell)); -+} -+ -+static int mhuv2_doorbell_send_data(struct mhuv2 *mhu, struct mbox_chan *chan, -+ void *arg) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&mhu->doorbell_pending_lock, flags); -+ -+ priv->pending = 1; -+ writel_relaxed(BIT(priv->doorbell), -+ &mhu->send->ch_wn[priv->ch_wn_idx].stat_set); -+ -+ spin_unlock_irqrestore(&mhu->doorbell_pending_lock, flags); -+ -+ return 0; -+} -+ -+static const struct mhuv2_protocol_ops mhuv2_doorbell_ops = { -+ .rx_startup = mhuv2_doorbell_rx_startup, -+ .rx_shutdown = mhuv2_doorbell_rx_shutdown, -+ .read_data = mhuv2_doorbell_read_data, -+ .last_tx_done = mhuv2_doorbell_last_tx_done, -+ .send_data = mhuv2_doorbell_send_data, -+}; -+#define IS_PROTOCOL_DOORBELL(_priv) (_priv->ops == &mhuv2_doorbell_ops) -+ -+/* ============= Data transfer transport protocol operations ================ */ -+ -+static int mhuv2_data_transfer_rx_startup(struct mhuv2 *mhu, -+ struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ int i = priv->ch_wn_idx + priv->windows - 1; -+ -+ /* -+ * The protocol mandates that all but the last status register must be -+ * masked. -+ */ -+ writel_relaxed(0xFFFFFFFF, &mhu->recv->ch_wn[i].mask_clear); -+ return 0; -+} -+ -+static void mhuv2_data_transfer_rx_shutdown(struct mhuv2 *mhu, -+ struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ int i = priv->ch_wn_idx + priv->windows - 1; -+ -+ writel_relaxed(0xFFFFFFFF, &mhu->recv->ch_wn[i].mask_set); -+} -+ -+static void *mhuv2_data_transfer_read_data(struct mhuv2 *mhu, -+ struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ const int windows = priv->windows; -+ struct arm_mhuv2_mbox_msg *msg; -+ u32 *data; -+ int i, idx; -+ -+ msg = kzalloc(sizeof(*msg) + windows * MHUV2_STAT_BYTES, GFP_KERNEL); -+ if (!msg) -+ return ERR_PTR(-ENOMEM); -+ -+ data = msg->data = msg + 1; -+ msg->len = windows * MHUV2_STAT_BYTES; -+ -+ /* -+ * Messages are expected in order of most significant word to least -+ * significant word. Refer mhuv2_data_transfer_send_data() for more -+ * details. -+ * -+ * We also need to read the stat register instead of stat_masked, as we -+ * masked all but the last window. -+ * -+ * Last channel window must be cleared as the final operation. Upon -+ * clearing the last channel window register, which is unmasked in -+ * data-transfer protocol, the interrupt is de-asserted. -+ */ -+ for (i = 0; i < windows; i++) { -+ idx = priv->ch_wn_idx + i; -+ data[windows - 1 - i] = readl_relaxed(&mhu->recv->ch_wn[idx].stat); -+ writel_relaxed(0xFFFFFFFF, &mhu->recv->ch_wn[idx].stat_clear); -+ } -+ -+ return msg; -+} -+ -+static void mhuv2_data_transfer_tx_startup(struct mhuv2 *mhu, -+ struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ int i = priv->ch_wn_idx + priv->windows - 1; -+ -+ /* Enable interrupts only for the last window */ -+ if (mhu->minor) { -+ writel_relaxed(0x1, &mhu->send->ch_wn[i].int_clr); -+ writel_relaxed(0x1, &mhu->send->ch_wn[i].int_en); -+ } -+} -+ -+static void mhuv2_data_transfer_tx_shutdown(struct mhuv2 *mhu, -+ struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ int i = priv->ch_wn_idx + priv->windows - 1; -+ -+ if (mhu->minor) -+ writel_relaxed(0x0, &mhu->send->ch_wn[i].int_en); -+} -+ -+static int mhuv2_data_transfer_last_tx_done(struct mhuv2 *mhu, -+ struct mbox_chan *chan) -+{ -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ int i = priv->ch_wn_idx + priv->windows - 1; -+ -+ /* Just checking the last channel window should be enough */ -+ return !readl_relaxed(&mhu->send->ch_wn[i].stat); -+} -+ -+/* -+ * Message will be transmitted from most significant to least significant word. -+ * This is to allow for messages shorter than channel windows to still trigger -+ * the receiver interrupt which gets activated when the last stat register is -+ * written. As an example, a 6-word message is to be written on a 4-channel MHU -+ * connection: Registers marked with '*' are masked, and will not generate an -+ * interrupt on the receiver side once written. -+ * -+ * u32 *data = [0x00000001], [0x00000002], [0x00000003], [0x00000004], -+ * [0x00000005], [0x00000006] -+ * -+ * ROUND 1: -+ * stat reg To write Write sequence -+ * [ stat 3 ] <- [0x00000001] 4 <- triggers interrupt on receiver -+ * [ stat 2 ] <- [0x00000002] 3 -+ * [ stat 1 ] <- [0x00000003] 2 -+ * [ stat 0 ] <- [0x00000004] 1 -+ * -+ * data += 4 // Increment data pointer by number of stat regs -+ * -+ * ROUND 2: -+ * stat reg To write Write sequence -+ * [ stat 3 ] <- [0x00000005] 2 <- triggers interrupt on receiver -+ * [ stat 2 ] <- [0x00000006] 1 -+ * [ stat 1 ] <- [0x00000000] -+ * [ stat 0 ] <- [0x00000000] -+ */ -+static int mhuv2_data_transfer_send_data(struct mhuv2 *mhu, -+ struct mbox_chan *chan, void *arg) -+{ -+ const struct arm_mhuv2_mbox_msg *msg = arg; -+ int bytes_left = msg->len, bytes_to_send, bytes_in_round, i; -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ int windows = priv->windows; -+ u32 *data = msg->data, word; -+ -+ while (bytes_left) { -+ if (!data[0]) { -+ dev_err(mhu->mbox.dev, "Data aligned at first window can't be zero to guarantee interrupt generation at receiver"); -+ return -EINVAL; -+ } -+ -+ while(!mhuv2_data_transfer_last_tx_done(mhu, chan)) -+ continue; -+ -+ bytes_in_round = min(bytes_left, (int)(windows * MHUV2_STAT_BYTES)); -+ -+ for (i = windows - 1; i >= 0; i--) { -+ /* Data less than windows can transfer ? */ -+ if (unlikely(bytes_in_round <= i * MHUV2_STAT_BYTES)) -+ continue; -+ -+ word = data[i]; -+ bytes_to_send = bytes_in_round & (MHUV2_STAT_BYTES - 1); -+ if (unlikely(bytes_to_send)) -+ word &= LSB_MASK(bytes_to_send); -+ else -+ bytes_to_send = MHUV2_STAT_BYTES; -+ -+ writel_relaxed(word, &mhu->send->ch_wn[priv->ch_wn_idx + windows - 1 - i].stat_set); -+ bytes_left -= bytes_to_send; -+ bytes_in_round -= bytes_to_send; -+ } -+ -+ data += windows; -+ } -+ -+ return 0; -+} -+ -+static const struct mhuv2_protocol_ops mhuv2_data_transfer_ops = { -+ .rx_startup = mhuv2_data_transfer_rx_startup, -+ .rx_shutdown = mhuv2_data_transfer_rx_shutdown, -+ .read_data = mhuv2_data_transfer_read_data, -+ .tx_startup = mhuv2_data_transfer_tx_startup, -+ .tx_shutdown = mhuv2_data_transfer_tx_shutdown, -+ .last_tx_done = mhuv2_data_transfer_last_tx_done, -+ .send_data = mhuv2_data_transfer_send_data, -+}; -+ -+/* Interrupt handlers */ -+ -+static struct mbox_chan *get_irq_chan_comb(struct mhuv2 *mhu, u32 *reg) -+{ -+ struct mbox_chan *chans = mhu->mbox.chans; -+ int channel = 0, i, offset = 0, windows, protocol, ch_wn; -+ u32 stat; -+ -+ for (i = 0; i < MHUV2_CMB_INT_ST_REG_CNT; i++) { -+ stat = readl_relaxed(reg + i); -+ if (!stat) -+ continue; -+ -+ ch_wn = i * MHUV2_STAT_BITS + __builtin_ctz(stat); -+ -+ for (i = 0; i < mhu->length; i += 2) { -+ protocol = mhu->protocols[i]; -+ windows = mhu->protocols[i + 1]; -+ -+ if (ch_wn >= offset + windows) { -+ if (protocol == DOORBELL) -+ channel += MHUV2_STAT_BITS * windows; -+ else -+ channel++; -+ -+ offset += windows; -+ continue; -+ } -+ -+ /* Return first chan of the window in doorbell mode */ -+ if (protocol == DOORBELL) -+ channel += MHUV2_STAT_BITS * (ch_wn - offset); -+ -+ return &chans[channel]; -+ } -+ } -+ -+ return ERR_PTR(-EIO); -+} -+ -+static irqreturn_t mhuv2_sender_interrupt(int irq, void *data) -+{ -+ struct mhuv2 *mhu = data; -+ struct device *dev = mhu->mbox.dev; -+ struct mhuv2_mbox_chan_priv *priv; -+ struct mbox_chan *chan; -+ unsigned long flags; -+ int i, found = 0; -+ u32 stat; -+ -+ chan = get_irq_chan_comb(mhu, mhu->send->chcomb_int_st); -+ if (IS_ERR(chan)) { -+ dev_warn(dev, "Failed to find channel for the Tx interrupt\n"); -+ return IRQ_NONE; -+ } -+ priv = chan->con_priv; -+ -+ if (!IS_PROTOCOL_DOORBELL(priv)) { -+ writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx + priv->windows - 1].int_clr); -+ -+ if (chan->cl) { -+ mbox_chan_txdone(chan, 0); -+ return IRQ_HANDLED; -+ } -+ -+ dev_warn(dev, "Tx interrupt Received on channel (%u) not currently attached to a mailbox client\n", -+ priv->ch_wn_idx); -+ return IRQ_NONE; -+ } -+ -+ /* Clear the interrupt first, so we don't miss any doorbell later */ -+ writel_relaxed(1, &mhu->send->ch_wn[priv->ch_wn_idx].int_clr); -+ -+ /* -+ * In Doorbell mode, make sure no new transitions happen while the -+ * interrupt handler is trying to find the finished doorbell tx -+ * operations, else we may think few of the transfers were complete -+ * before they actually were. -+ */ -+ spin_lock_irqsave(&mhu->doorbell_pending_lock, flags); -+ -+ /* -+ * In case of doorbell mode, the first channel of the window is returned -+ * by get_irq_chan_comb(). Find all the pending channels here. -+ */ -+ stat = readl_relaxed(&mhu->send->ch_wn[priv->ch_wn_idx].stat); -+ -+ for (i = 0; i < MHUV2_STAT_BITS; i++) { -+ priv = chan[i].con_priv; -+ -+ /* Find cases where pending was 1, but stat's bit is cleared */ -+ if (priv->pending ^ ((stat >> i) & 0x1)) { -+ BUG_ON(!priv->pending); -+ -+ if (!chan->cl) { -+ dev_warn(dev, "Tx interrupt received on doorbell (%u : %u) channel not currently attached to a mailbox client\n", -+ priv->ch_wn_idx, i); -+ continue; -+ } -+ -+ mbox_chan_txdone(&chan[i], 0); -+ priv->pending = 0; -+ found++; -+ } -+ } -+ -+ spin_unlock_irqrestore(&mhu->doorbell_pending_lock, flags); -+ -+ if (!found) { -+ /* -+ * We may have already processed the doorbell in the previous -+ * iteration if the interrupt came right after we cleared it but -+ * before we read the stat register. -+ */ -+ dev_dbg(dev, "Couldn't find the doorbell (%u) for the Tx interrupt interrupt\n", -+ priv->ch_wn_idx); -+ return IRQ_NONE; -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static struct mbox_chan *get_irq_chan_comb_rx(struct mhuv2 *mhu) -+{ -+ struct mhuv2_mbox_chan_priv *priv; -+ struct mbox_chan *chan; -+ u32 stat; -+ -+ chan = get_irq_chan_comb(mhu, mhu->recv->chcomb_int_st); -+ if (IS_ERR(chan)) -+ return chan; -+ -+ priv = chan->con_priv; -+ if (!IS_PROTOCOL_DOORBELL(priv)) -+ return chan; -+ -+ /* -+ * In case of doorbell mode, the first channel of the window is returned -+ * by the routine. Find the exact channel here. -+ */ -+ stat = readl_relaxed(&mhu->recv->ch_wn[priv->ch_wn_idx].stat_masked); -+ BUG_ON(!stat); -+ -+ return chan + __builtin_ctz(stat); -+} -+ -+static struct mbox_chan *get_irq_chan_stat_rx(struct mhuv2 *mhu) -+{ -+ struct mbox_chan *chans = mhu->mbox.chans; -+ struct mhuv2_mbox_chan_priv *priv; -+ u32 stat; -+ int i = 0; -+ -+ while (i < mhu->mbox.num_chans) { -+ priv = chans[i].con_priv; -+ stat = readl_relaxed(&mhu->recv->ch_wn[priv->ch_wn_idx].stat_masked); -+ -+ if (stat) { -+ if (IS_PROTOCOL_DOORBELL(priv)) -+ i += __builtin_ctz(stat); -+ return &chans[i]; -+ } -+ -+ i += IS_PROTOCOL_DOORBELL(priv) ? MHUV2_STAT_BITS : 1; -+ } -+ -+ return ERR_PTR(-EIO); -+} -+ -+static struct mbox_chan *get_irq_chan_rx(struct mhuv2 *mhu) -+{ -+ if (!mhu->minor) -+ return get_irq_chan_stat_rx(mhu); -+ -+ return get_irq_chan_comb_rx(mhu); -+} -+ -+static irqreturn_t mhuv2_receiver_interrupt(int irq, void *arg) -+{ -+ struct mhuv2 *mhu = arg; -+ struct mbox_chan *chan = get_irq_chan_rx(mhu); -+ struct device *dev = mhu->mbox.dev; -+ struct mhuv2_mbox_chan_priv *priv; -+ int ret = IRQ_NONE; -+ void *data; -+ -+ if (IS_ERR(chan)) { -+ dev_warn(dev, "Failed to find channel for the rx interrupt\n"); -+ return IRQ_NONE; -+ } -+ priv = chan->con_priv; -+ -+ /* Read and clear the data first */ -+ data = priv->ops->read_data(mhu, chan); -+ -+ if (!chan->cl) { -+ dev_warn(dev, "Received data on channel (%u) not currently attached to a mailbox client\n", -+ priv->ch_wn_idx); -+ } else if (IS_ERR(data)) { -+ dev_err(dev, "Failed to read data: %lu\n", PTR_ERR(data)); -+ } else { -+ mbox_chan_received_data(chan, data); -+ ret = IRQ_HANDLED; -+ } -+ -+ kfree(data); -+ return ret; -+} -+ -+/* Sender and receiver ops */ -+static bool mhuv2_sender_last_tx_done(struct mbox_chan *chan) -+{ -+ struct mhuv2 *mhu = mhu_from_mbox(chan->mbox); -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ return priv->ops->last_tx_done(mhu, chan); -+} -+ -+static int mhuv2_sender_send_data(struct mbox_chan *chan, void *data) -+{ -+ struct mhuv2 *mhu = mhu_from_mbox(chan->mbox); -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ if (!priv->ops->last_tx_done(mhu, chan)) -+ return -EBUSY; -+ -+ return priv->ops->send_data(mhu, chan, data); -+} -+ -+static int mhuv2_sender_startup(struct mbox_chan *chan) -+{ -+ struct mhuv2 *mhu = mhu_from_mbox(chan->mbox); -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ if (priv->ops->tx_startup) -+ priv->ops->tx_startup(mhu, chan); -+ return 0; -+} -+ -+static void mhuv2_sender_shutdown(struct mbox_chan *chan) -+{ -+ struct mhuv2 *mhu = mhu_from_mbox(chan->mbox); -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ if (priv->ops->tx_shutdown) -+ priv->ops->tx_shutdown(mhu, chan); -+} -+ -+static const struct mbox_chan_ops mhuv2_sender_ops = { -+ .send_data = mhuv2_sender_send_data, -+ .startup = mhuv2_sender_startup, -+ .shutdown = mhuv2_sender_shutdown, -+ .last_tx_done = mhuv2_sender_last_tx_done, -+}; -+ -+static int mhuv2_receiver_startup(struct mbox_chan *chan) -+{ -+ struct mhuv2 *mhu = mhu_from_mbox(chan->mbox); -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ return priv->ops->rx_startup(mhu, chan); -+} -+ -+static void mhuv2_receiver_shutdown(struct mbox_chan *chan) -+{ -+ struct mhuv2 *mhu = mhu_from_mbox(chan->mbox); -+ struct mhuv2_mbox_chan_priv *priv = chan->con_priv; -+ -+ priv->ops->rx_shutdown(mhu, chan); -+} -+ -+static int mhuv2_receiver_send_data(struct mbox_chan *chan, void *data) -+{ -+ dev_err(chan->mbox->dev, -+ "Trying to transmit on a receiver MHU frame\n"); -+ return -EIO; -+} -+ -+static bool mhuv2_receiver_last_tx_done(struct mbox_chan *chan) -+{ -+ dev_err(chan->mbox->dev, "Trying to Tx poll on a receiver MHU frame\n"); -+ return true; -+} -+ -+static const struct mbox_chan_ops mhuv2_receiver_ops = { -+ .send_data = mhuv2_receiver_send_data, -+ .startup = mhuv2_receiver_startup, -+ .shutdown = mhuv2_receiver_shutdown, -+ .last_tx_done = mhuv2_receiver_last_tx_done, -+}; -+ -+static struct mbox_chan *mhuv2_mbox_of_xlate(struct mbox_controller *mbox, -+ const struct of_phandle_args *pa) -+{ -+ struct mhuv2 *mhu = mhu_from_mbox(mbox); -+ struct mbox_chan *chans = mbox->chans; -+ int channel = 0, i, offset, doorbell, protocol, windows; -+ -+ if (pa->args_count != 2) -+ return ERR_PTR(-EINVAL); -+ -+ offset = pa->args[0]; -+ doorbell = pa->args[1]; -+ if (doorbell >= MHUV2_STAT_BITS) -+ goto out; -+ -+ for (i = 0; i < mhu->length; i += 2) { -+ protocol = mhu->protocols[i]; -+ windows = mhu->protocols[i + 1]; -+ -+ if (protocol == DOORBELL) { -+ if (offset < windows) -+ return &chans[channel + MHUV2_STAT_BITS * offset + doorbell]; -+ -+ channel += MHUV2_STAT_BITS * windows; -+ offset -= windows; -+ } else { -+ if (offset == 0) { -+ if (doorbell) -+ goto out; -+ -+ return &chans[channel]; -+ } -+ -+ channel++; -+ offset--; -+ } -+ } -+ -+out: -+ dev_err(mbox->dev, "Couldn't xlate to a valid channel (%d: %d)\n", -+ pa->args[0], doorbell); -+ return ERR_PTR(-ENODEV); -+} -+ -+static int mhuv2_verify_protocol(struct mhuv2 *mhu) -+{ -+ struct device *dev = mhu->mbox.dev; -+ int protocol, windows, channels = 0, total_windows = 0, i; -+ -+ for (i = 0; i < mhu->length; i += 2) { -+ protocol = mhu->protocols[i]; -+ windows = mhu->protocols[i + 1]; -+ -+ if (!windows) { -+ dev_err(dev, "Window size can't be zero (%d)\n", i); -+ return -EINVAL; -+ } -+ total_windows += windows; -+ -+ if (protocol == DOORBELL) { -+ channels += MHUV2_STAT_BITS * windows; -+ } else if (protocol == DATA_TRANSFER) { -+ channels++; -+ } else { -+ dev_err(dev, "Invalid protocol (%d) present in %s property at index %d\n", -+ protocol, MHUV2_PROTOCOL_PROP, i); -+ return -EINVAL; -+ } -+ } -+ -+ if (total_windows > mhu->windows) { -+ dev_err(dev, "Channel windows can't be more than what's implemented by the hardware ( %d: %d)\n", -+ total_windows, mhu->windows); -+ return -EINVAL; -+ } -+ -+ mhu->mbox.num_chans = channels; -+ return 0; -+} -+ -+static int mhuv2_allocate_channels(struct mhuv2 *mhu) -+{ -+ struct mbox_controller *mbox = &mhu->mbox; -+ struct mhuv2_mbox_chan_priv *priv; -+ struct device *dev = mbox->dev; -+ struct mbox_chan *chans; -+ int protocol, windows = 0, next_window = 0, i, j, k; -+ -+ chans = devm_kcalloc(dev, mbox->num_chans, sizeof(*chans), GFP_KERNEL); -+ if (!chans) -+ return -ENOMEM; -+ -+ mbox->chans = chans; -+ -+ for (i = 0; i < mhu->length; i += 2) { -+ next_window += windows; -+ -+ protocol = mhu->protocols[i]; -+ windows = mhu->protocols[i + 1]; -+ -+ if (protocol == DATA_TRANSFER) { -+ priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ priv->ch_wn_idx = next_window; -+ priv->ops = &mhuv2_data_transfer_ops; -+ priv->windows = windows; -+ chans++->con_priv = priv; -+ continue; -+ } -+ -+ for (j = 0; j < windows; j++) { -+ for (k = 0; k < MHUV2_STAT_BITS; k++) { -+ priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL); -+ if (!priv) -+ return -ENOMEM; -+ -+ priv->ch_wn_idx = next_window + j; -+ priv->ops = &mhuv2_doorbell_ops; -+ priv->doorbell = k; -+ chans++->con_priv = priv; -+ } -+ -+ /* -+ * Permanently enable interrupt as we can't -+ * control it per doorbell. -+ */ -+ if (mhu->frame == SENDER_FRAME && mhu->minor) -+ writel_relaxed(0x1, &mhu->send->ch_wn[priv->ch_wn_idx].int_en); -+ } -+ } -+ -+ /* Make sure we have initialized all channels */ -+ BUG_ON(chans - mbox->chans != mbox->num_chans); -+ -+ return 0; -+} -+ -+static int mhuv2_parse_channels(struct mhuv2 *mhu) -+{ -+ struct device *dev = mhu->mbox.dev; -+ const struct device_node *np = dev->of_node; -+ int ret, count; -+ u32 *protocols; -+ -+ count = of_property_count_u32_elems(np, MHUV2_PROTOCOL_PROP); -+ if (count <= 0 || count % 2) { -+ dev_err(dev, "Invalid %s property (%d)\n", MHUV2_PROTOCOL_PROP, -+ count); -+ return -EINVAL; -+ } -+ -+ protocols = devm_kmalloc_array(dev, count, sizeof(*protocols), GFP_KERNEL); -+ if (!protocols) -+ return -ENOMEM; -+ -+ ret = of_property_read_u32_array(np, MHUV2_PROTOCOL_PROP, protocols, count); -+ if (ret) { -+ dev_err(dev, "Failed to read %s property: %d\n", -+ MHUV2_PROTOCOL_PROP, ret); -+ return ret; -+ } -+ -+ mhu->protocols = protocols; -+ mhu->length = count; -+ -+ ret = mhuv2_verify_protocol(mhu); -+ if (ret) -+ return ret; -+ -+ return mhuv2_allocate_channels(mhu); -+} -+ -+static int mhuv2_tx_init(struct amba_device *adev, struct mhuv2 *mhu, -+ void __iomem *reg) -+{ -+ struct device *dev = mhu->mbox.dev; -+ int ret, i; -+ -+ mhu->frame = SENDER_FRAME; -+ mhu->mbox.ops = &mhuv2_sender_ops; -+ mhu->send = reg; -+ -+ mhu->windows = readl_relaxed_bitfield(&mhu->send->mhu_cfg, num_ch); -+ mhu->minor = readl_relaxed_bitfield(&mhu->send->aidr, arch_minor_rev); -+ -+ spin_lock_init(&mhu->doorbell_pending_lock); -+ -+ /* -+ * For minor version 1 and forward, tx interrupt is provided by -+ * the controller. -+ */ -+ if (mhu->minor && adev->irq[0]) { -+ ret = devm_request_threaded_irq(dev, adev->irq[0], NULL, -+ mhuv2_sender_interrupt, -+ IRQF_ONESHOT, "mhuv2-tx", mhu); -+ if (ret) { -+ dev_err(dev, "Failed to request tx IRQ, fallback to polling mode: %d\n", -+ ret); -+ } else { -+ mhu->mbox.txdone_irq = true; -+ mhu->mbox.txdone_poll = false; -+ mhu->irq = adev->irq[0]; -+ -+ writel_relaxed_bitfield(1, &mhu->send->int_en, chcomb); -+ -+ /* Disable all channel interrupts */ -+ for (i = 0; i < mhu->windows; i++) -+ writel_relaxed(0x0, &mhu->send->ch_wn[i].int_en); -+ -+ goto out; -+ } -+ } -+ -+ mhu->mbox.txdone_irq = false; -+ mhu->mbox.txdone_poll = true; -+ mhu->mbox.txpoll_period = 1; -+ -+out: -+ /* Wait for receiver to be ready */ -+ writel_relaxed(0x1, &mhu->send->access_request); -+ while (!readl_relaxed(&mhu->send->access_ready)) -+ continue; -+ -+ return 0; -+} -+ -+static int mhuv2_rx_init(struct amba_device *adev, struct mhuv2 *mhu, -+ void __iomem *reg) -+{ -+ struct device *dev = mhu->mbox.dev; -+ int ret, i; -+ -+ mhu->frame = RECEIVER_FRAME; -+ mhu->mbox.ops = &mhuv2_receiver_ops; -+ mhu->recv = reg; -+ -+ mhu->windows = readl_relaxed_bitfield(&mhu->recv->mhu_cfg, num_ch); -+ mhu->minor = readl_relaxed_bitfield(&mhu->recv->aidr, arch_minor_rev); -+ -+ mhu->irq = adev->irq[0]; -+ if (!mhu->irq) { -+ dev_err(dev, "Missing receiver IRQ\n"); -+ return -EINVAL; -+ } -+ -+ ret = devm_request_threaded_irq(dev, mhu->irq, NULL, -+ mhuv2_receiver_interrupt, IRQF_ONESHOT, -+ "mhuv2-rx", mhu); -+ if (ret) { -+ dev_err(dev, "Failed to request rx IRQ\n"); -+ return ret; -+ } -+ -+ /* Mask all the channel windows */ -+ for (i = 0; i < mhu->windows; i++) -+ writel_relaxed(0xFFFFFFFF, &mhu->recv->ch_wn[i].mask_set); -+ -+ if (mhu->minor) -+ writel_relaxed_bitfield(1, &mhu->recv->int_en, chcomb); -+ -+ return 0; -+} -+ -+static int mhuv2_probe(struct amba_device *adev, const struct amba_id *id) -+{ -+ struct device *dev = &adev->dev; -+ const struct device_node *np = dev->of_node; -+ struct mhuv2 *mhu; -+ void __iomem *reg; -+ int ret = -EINVAL; -+ -+ reg = devm_of_iomap(dev, dev->of_node, 0, NULL); -+ if (!reg) -+ return -ENOMEM; -+ -+ mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL); -+ if (!mhu) -+ return -ENOMEM; -+ -+ mhu->mbox.dev = dev; -+ mhu->mbox.of_xlate = mhuv2_mbox_of_xlate; -+ -+ if (of_device_is_compatible(np, "arm,mhuv2-tx")) -+ ret = mhuv2_tx_init(adev, mhu, reg); -+ else if (of_device_is_compatible(np, "arm,mhuv2-rx")) -+ ret = mhuv2_rx_init(adev, mhu, reg); -+ else -+ dev_err(dev, "Invalid compatible property\n"); -+ -+ if (ret) -+ return ret; -+ -+ /* Channel windows can't be 0 */ -+ BUG_ON(!mhu->windows); -+ -+ ret = mhuv2_parse_channels(mhu); -+ if (ret) -+ return ret; -+ -+ amba_set_drvdata(adev, mhu); -+ -+ ret = devm_mbox_controller_register(dev, &mhu->mbox); -+ if (ret) -+ dev_err(dev, "failed to register ARM MHUv2 driver %d\n", ret); -+ -+ return ret; -+} -+ -+static int mhuv2_remove(struct amba_device *adev) -+{ -+ struct mhuv2 *mhu = amba_get_drvdata(adev); -+ -+ if (mhu->frame == SENDER_FRAME) -+ writel_relaxed(0x0, &mhu->send->access_request); -+ -+ return 0; -+} -+ -+static struct amba_id mhuv2_ids[] = { -+ { -+ /* 2.0 */ -+ .id = 0xbb0d1, -+ .mask = 0xfffff, -+ }, -+ { -+ /* 2.1 */ -+ .id = 0xbb076, -+ .mask = 0xfffff, -+ }, -+ { 0, 0 }, -+}; -+MODULE_DEVICE_TABLE(amba, mhuv2_ids); -+ -+static struct amba_driver mhuv2_driver = { -+ .drv = { -+ .name = "arm-mhuv2", -+ }, -+ .id_table = mhuv2_ids, -+ .probe = mhuv2_probe, -+ .remove = mhuv2_remove, -+}; -+module_amba_driver(mhuv2_driver); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_DESCRIPTION("ARM MHUv2 Driver"); -+MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>"); -+MODULE_AUTHOR("Tushar Khandelwal <tushar.khandelwal@arm.com>"); -diff --git a/include/linux/mailbox/arm_mhuv2_message.h b/include/linux/mailbox/arm_mhuv2_message.h -new file mode 100644 -index 000000000000..821b9d96daa4 ---- /dev/null -+++ b/include/linux/mailbox/arm_mhuv2_message.h -@@ -0,0 +1,20 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * ARM MHUv2 Mailbox Message -+ * -+ * Copyright (C) 2020 Arm Ltd. -+ * Copyright (C) 2020 Linaro Ltd. -+ */ -+ -+#ifndef _LINUX_ARM_MHUV2_MESSAGE_H_ -+#define _LINUX_ARM_MHUV2_MESSAGE_H_ -+ -+#include <linux/types.h> -+ -+/* Data structure for data-transfer protocol */ -+struct arm_mhuv2_mbox_msg { -+ void *data; -+ size_t len; -+}; -+ -+#endif /* _LINUX_ARM_MHUV2_MESSAGE_H_ */ --- -2.17.1 - |