linux-kernel.vger.kernel.org archive mirror
 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: 22+ 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-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-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).