All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tushar Khandelwal <tushar.khandelwal@arm.com>
To: linux-kernel@vger.kernel.org
Cc: tushar.2nov@gmail.com, morten_bp@live.dk,
	jassisinghbrar@gmail.com, nd@arm.com,
	Morten Borup Petersen <morten.petersen@arm.com>,
	Tushar Khandelwal <tushar.khandelwal@arm.com>,
	devicetree@vger.kernel.org
Subject: [PATCH 4/4] mailbox: arm_mhuv2: add multi word transport protocol operations
Date: Wed, 17 Jul 2019 20:26:16 +0100	[thread overview]
Message-ID: <20190717192616.1731-5-tushar.khandelwal@arm.com> (raw)
In-Reply-To: <20190717192616.1731-1-tushar.khandelwal@arm.com>

From: Morten Borup Petersen <morten.petersen@arm.com>

When in multi-word mode, the mailbox controller will provide a single
mailbox. It is required that the MHU device has at least 2 channel windows
available for the MHU to function in multi-word mode.

Transmitting and receiving data through the mailbox framework in
multi-word mode is done through a struct arm_mbox_msg.

Signed-off-by: Morten Borup Petersen <morten.petersen@arm.com>
Signed-off-by: Tushar Khandelwal <tushar.khandelwal@arm.com>
Cc: jassisinghbrar@gmail.com
Cc: devicetree@vger.kernel.org
---
 drivers/mailbox/arm_mhu_v2.c | 225 +++++++++++++++++++++++++++++++++++
 1 file changed, 225 insertions(+)

diff --git a/drivers/mailbox/arm_mhu_v2.c b/drivers/mailbox/arm_mhu_v2.c
index 0e3fa5917925..324b19bdb28a 100644
--- a/drivers/mailbox/arm_mhu_v2.c
+++ b/drivers/mailbox/arm_mhu_v2.c
@@ -430,6 +430,228 @@ static const struct mhuv2_ops mhuv2_single_word_ops = {
 };
 /* ========================================================================== */
 
+/* ================ Multi word transport protocol operations ================ */
+static inline int mhuv2_read_data_multi_word(struct arm_mhuv2 *mhuv2,
+					     struct mbox_chan *chan,
+					     struct arm_mbox_msg *msg)
+{
+	int ch;
+	const int channels =
+		readl_relaxed_bitfield(&mhuv2->reg.recv->MHU_CFG, NUM_CH);
+
+	msg->data = kzalloc(MHUV2_STAT_BYTES * channels, GFP_KERNEL);
+
+	for (ch = 0; ch < channels; ch++) {
+		/*
+		 * Messages are expected to be received in order of most
+		 * significant word to least significant word.
+		 * (see mhuv2_send_data_multi_word)
+		 */
+		const mhuv2_stat_reg_t word =
+			readl_relaxed(&mhuv2->reg.recv->channel[ch].STAT);
+		((mhuv2_stat_reg_t *)msg->data)[channels - 1 - ch] = word;
+	}
+
+	msg->len = channels * MHUV2_STAT_BYTES;
+	return 0;
+}
+
+static inline int mhuv2_clear_data_multi_word(struct arm_mhuv2 *mhuv2,
+					      struct mbox_chan *chan,
+					      struct arm_mbox_msg *msg)
+{
+	int ch;
+	const int channels =
+		readl_relaxed_bitfield(&mhuv2->reg.recv->MHU_CFG, NUM_CH);
+
+	for (ch = 0; ch < channels; ch++) {
+		/*
+		 * Last channel window must be cleared as the final operation.
+		 * Upon clearing the last channel window register, which is
+		 * unmasked in multi-word mode, the interrupt is deasserted.
+		 */
+		writel_relaxed(
+			readl_relaxed(&mhuv2->reg.recv->channel[ch].STAT),
+			&mhuv2->reg.recv->channel[ch].STAT_CLEAR);
+	}
+	return 0;
+}
+
+static inline int __mhuv2_mw_bytes_to_send(const int bytes_in_round,
+					    const int bytes_left)
+{
+	/*
+	 * Bytes to send on the current channel will always be MHUV2_STAT_BYTES
+	 * unless in the last round and
+	 *	msg->len % MHUV2_STAT_BYTES != 0
+	 */
+	if (bytes_in_round % MHUV2_STAT_BYTES != 0) {
+		const int bts = bytes_left % MHUV2_STAT_BYTES;
+		return bts == 0 ? MHUV2_STAT_BYTES : bts;
+	} else {
+		return MHUV2_STAT_BYTES;
+	}
+}
+
+static inline int mhuv2_send_data_multi_word(struct arm_mhuv2 *mhuv2,
+					     struct mbox_chan *chan,
+					     const struct arm_mbox_msg *msg)
+{
+	/*
+	 * Message will be transmitted from most significant to least
+	 * significant word. This is to allow for messages shorter than
+	 * $channels 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.
+	 *
+	 * uint32_t *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]
+	 */
+	int bytes_left, bytes_to_send, i, ch_idx;
+	const int ch_windows =
+		readl_relaxed_bitfield(&mhuv2->reg.recv->MHU_CFG, NUM_CH);
+	const size_t round_capacity = ch_windows * MHUV2_STAT_BYTES;
+
+	bytes_left = msg->len;
+	mhuv2_stat_reg_t *data = msg->data;
+
+	while (bytes_left > 0) {
+		/* Note: Each entry of this loop indicates a new ROUND */
+		if (*(u32 *)data == 0) {
+			dev_err(mhuv2->dev,
+				"values in *data aligned on NUM_STAT boundaries must not be zero to ensure that receiver interrupt is triggered\n",
+				ch_windows);
+			return -EINVAL;
+		}
+
+		const int bytes_in_round = bytes_left > round_capacity ?
+						   round_capacity :
+						   bytes_left;
+
+		for (i = (ch_windows - 1); i >= 0; i--) {
+			ch_idx = ch_windows - 1 - i;
+			/*
+			 * Check whether data should be transmitted in register
+			 * of index 'ch'.
+			 */
+			if (bytes_in_round > (i * MHUV2_STAT_BYTES)) {
+				mhuv2_stat_reg_t word = data[i];
+
+				bytes_to_send = __mhuv2_mw_bytes_to_send(
+					bytes_in_round, bytes_left);
+
+				if (bytes_to_send != MHUV2_STAT_BYTES) {
+					word &= LSB_MASK(bytes_to_send *
+							 __CHAR_BIT__);
+				}
+				while (readl_relaxed(
+					       &mhuv2->reg.send->channel[ch_idx]
+							.STAT) != 0)
+					continue;
+
+				writel_relaxed(
+				    word,
+				    &mhuv2->reg.send->channel[ch_idx].STAT_SET);
+				bytes_left -= bytes_to_send;
+			}
+		}
+
+		data += ch_windows;
+
+		for (ch_idx = 0; ch_idx < ch_windows; ch_idx++) {
+			while (readl_relaxed(
+				   &mhuv2->reg.send->channel[ch_idx].STAT) != 0)
+				continue;
+		}
+	}
+	return 0;
+}
+
+
+static inline int mhuv2_last_tx_done_multi_word(struct arm_mhuv2 *mhuv2,
+						struct mbox_chan *chan)
+{
+	int ch_idx;
+	bool tx_done = true;
+
+	for (ch_idx = 0;
+	     ch_idx < readl_relaxed_bitfield(&mhuv2->reg.send->MHU_CFG, NUM_CH);
+	     ch_idx++) {
+		tx_done &= readl_relaxed(
+				   &mhuv2->reg.send->channel[ch_idx].STAT) == 0;
+	}
+	return tx_done;
+}
+
+static inline int mhuv2_setup_multi_word(struct arm_mhuv2 *mhuv2)
+{
+	int ret, i;
+
+	const u32 channel_windows =
+		readl_relaxed_bitfield(mhuv2->frame == RECEIVER_FRAME ?
+					       &mhuv2->reg.recv->MHU_CFG :
+					       &mhuv2->reg.send->MHU_CFG,
+				       NUM_CH);
+	if (channel_windows < 2) {
+		dev_err(mhuv2->dev,
+			"Error: at least 2 MHU channel windows are required for using the multi-word transfer protocol");
+		return -ENODEV;
+	}
+
+	if (mhuv2->frame == RECEIVER_FRAME) {
+		/*
+		 * The multi-word transport protocol mandates that all but
+		 * the last status register must be masked.
+		 */
+		for (i = 0; i < (channel_windows - 1); i++) {
+			writel_relaxed(-1,
+				       &mhuv2->reg.recv->channel[i].MASK_SET);
+		}
+	}
+
+	mhuv2->mbox.num_chans = 1;
+	mhuv2->mbox.chans =
+		devm_kzalloc(mhuv2->dev,
+			     mhuv2->mbox.num_chans * sizeof(struct mbox_chan),
+			     GFP_KERNEL);
+
+	return 0;
+}
+
+static inline struct mbox_chan *
+	mhuv2_get_active_mbox_chan_multi_word(struct arm_mhuv2 *mhuv2)
+{
+	return &mhuv2->mbox.chans[0];
+}
+
+static const struct mhuv2_ops mhuv2_multi_word_ops = {
+	.read_data = mhuv2_read_data_multi_word,
+	.clear_data = mhuv2_clear_data_multi_word,
+	.send_data = mhuv2_send_data_multi_word,
+	.setup = mhuv2_setup_multi_word,
+	.last_tx_done = mhuv2_last_tx_done_multi_word,
+	.get_active_mbox_chan = mhuv2_get_active_mbox_chan_multi_word,
+};
+/* ========================================================================== */
+
 /* =================== Doorbell transport protocol operations =============== */
 
 static inline int mhuv2_read_data_doorbell(struct arm_mhuv2 *mhuv2,
@@ -740,6 +962,9 @@ static int mhuv2_probe(struct amba_device *adev, const struct amba_id *id)
 
 	/* Assign transport protocol-specific operations */
 	switch (mhuv2->protocol) {
+	case MULTI_WORD:
+		mhuv2->ops = &mhuv2_multi_word_ops;
+		break;
 	case SINGLE_WORD:
 		mhuv2->ops = &mhuv2_single_word_ops;
 		break;
-- 
2.17.1


      parent reply	other threads:[~2019-07-17 19:26 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-17 19:26 [PATCH 0/4] Arm MHUv2 Mailbox Controller Driver Tushar Khandelwal
2019-07-17 19:26 ` [PATCH 1/4] mailbox: arm_mhuv2: add device tree binding documentation Tushar Khandelwal
2019-07-21 21:58   ` Jassi Brar
2019-07-25  5:49     ` Jassi Brar
2019-07-28 21:28       ` Morten Borup Petersen
2019-07-31  7:31         ` Jassi Brar
2019-08-02 10:41           ` Morten Borup Petersen
2019-08-13 16:36             ` Jassi Brar
2019-08-13 16:36               ` Jassi Brar
2019-08-14 10:05               ` Sudeep Holla
2019-08-14 14:52                 ` Jassi Brar
2019-08-14 16:51                   ` Sudeep Holla
2019-08-14 22:22                     ` tushar.khandelwal
2019-08-14 22:20               ` tushar.khandelwal
2019-08-02 10:53       ` Sudeep Holla
2019-08-07 11:11         ` Tushar Khandelwal
2019-07-28 21:27     ` Morten Borup Petersen
2019-08-02 10:59     ` Sudeep Holla
2019-08-07 11:17       ` Tushar Khandelwal
2019-08-07 11:32         ` Tushar K
2019-08-08 10:31         ` Morten Borup Petersen
2019-07-17 19:26 ` [PATCH 2/4] mailbox: arm_mhuv2: add arm mhuv2 driver Tushar Khandelwal
2019-07-17 19:26 ` [PATCH 3/4] mailbox: arm_mhuv2: add doorbell transport protocol operations Tushar Khandelwal
2019-07-17 19:26 ` Tushar Khandelwal [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190717192616.1731-5-tushar.khandelwal@arm.com \
    --to=tushar.khandelwal@arm.com \
    --cc=devicetree@vger.kernel.org \
    --cc=jassisinghbrar@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=morten.petersen@arm.com \
    --cc=morten_bp@live.dk \
    --cc=nd@arm.com \
    --cc=tushar.2nov@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.