All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/6] Introduce a unified API for SCMI Server testing
@ 2022-08-16  7:24 ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Hi all,

This series aims to introduce a new SCMI unified userspace interface meant
to ease testing an SCMI Server implementation for compliance, fuzzing etc.,
from the perspective of the OSPM agent (non-secure world only ...)

It is proposed as a testing/development facility, it is NOT meant to be a
feature to use in production, but only enabled in Kconfig for test
deployments.

Currently an SCMI Compliance Suite like the one at [1] can only work by
injecting SCMI messages at the SCMI transport layer using the mailbox test
driver (CONFIG_MAILBOX_TEST) via its few debugfs entries and looking at
the related replies from the SCMI backend Server.

This approach has a few drawbacks:

- the SCMI Server under test MUST be reachable through a mailbox based
  SCMI transport: any other SCMI Server placement is not possible (like in
  a VM reachable via SCMI Virtio). In order to cover other placements in
  the current scenario we should write some sort of test driver for each
  and every existent SCMI transport and for any future additional transport
  ...this clearly does not scale.

- even in the mailbox case the userspace Compliance suite cannot simply
  send and receive bare SCMI messages BUT it has to properly lay them out
  into the shared memory exposed by the mailbox test driver as expected by
  the transport definitions. In other words such a userspace test
  application has to, not only use a proper transport driver for the system
  at hand, but it also has to have a comprehensive knowledge of the
  internals of the underlying transport in order to operate.

- last but not least, the system under test has to be specifically
  configured and built, in terms of Kconfig and DT, to perform such kind of
  testing, it cannot be used for anything else, which is unfortunate for
  CI/CD deployments.

This series introduces a new SCMI Raw mode support feature that, when
configured and enabled exposes a new interface in debugfs through which:

- a userspace application can inject bare SCMI binary messages into the
  SCMI core stack: such messages will be routed by the SCMI regular kernel
  stack to the backend Server using the currently configured transport
  transparently: in other words you can test the SCMI server no matter
  where it is placed, as long as it is reachable from the currently
  configured SCMI stack.
  Same goes the other way around on the reading path: any SCMI server reply
  can be read as a bare SCMI binary message from the same debugfs path.

- as a direct consequence of this way of injecting bare messages in the
  middle of the SCMI stack (instead of beneath it at the transport layer)
  the user application has to handle only bare SCMI messages without having
  to worry about the specific underlying transport internals that will be
  taken care of by the SCMI core stack itself using its own machinery,
  without duplicating such logic.

- last but not least, even when the SCMI Raw mode is enabled at compile
  time, the system under test DT configuration does NOT need any change and
  the mode itself is NOT anyway active until explicitly enabled at run-time
  via debugfs: as a consequence it does NOT interfere with the SCMI regular
  drivers stack until runtime enabled.
  You can boot a normal SCMI-based system and then switch to Raw mode only
  if you intend to run an SCMI Server oriented test suite (and possibly
  switch back to normal at the end....but here there are some issues/
  caveats on this resurrection path :D ...see down below)

A quick and trivial example from the shell...reading from a sensor
injecting a properly crafted packet in raw mode:

	# ENABLE RAW MODE...
	root@deb-buster-arm64:~# echo 1 > /sys/kernel/debug/scmi_raw/enable

	# INJECT THE SENSOR_READING MESSAGE FOR SENSOR ID=1 (binary little endian)
	root@deb-buster-arm64:~# echo -e -n \\x06\\x54\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00 > /sys/kernel/debug/scmi_raw/message

	# READING BACK THE REPLY...
	root@deb-buster-arm64:~# cat /sys/kernel/debug/scmi_raw/message | od --endian=little -t x4
	0000000 00005406 00000000 00000335 00000000
	0000020

while doing that, since Raw mode makes (partial) use of the regular SCMI
stack, you can observe the messages going through the SCMI stack with the
usual traces:

              bash-329     [000] ..... 14183.446808: scmi_msg_dump: pt=15 t=CMND msg_id=06 seq=0000 s=0 pyld=0100000000000000
   irq/35-mhu_db_l-81      [000] ..... 14183.447809: scmi_msg_dump: pt=15 t=RESP msg_id=06 seq=0000 s=0 pyld=3503000000000000


..trying to read in async when the backend server does NOT supports asyncs:

	# AN ASYNC SENSOR READING REQUEST...
	root@deb-buster-arm64:~# echo -e -n \\x06\\x54\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00 > /sys/kernel/debug/scmi_raw/message_async

              bash-329     [000] ..... 16415.938739: scmi_msg_dump: pt=15 t=CMND msg_id=06 seq=0000 s=0 pyld=0100000001000000
   irq/35-mhu_db_l-81      [000] ..... 16415.944129: scmi_msg_dump: pt=15 t=RESP msg_id=06 seq=0000 s=-1 pyld=

	# RETURNS A STATUS -1 FROM THE SERVER NOT SUPPORTING IT
	root@deb-buster-arm64:~# cat /sys/kernel/debug/scmi_raw/message | od --endian=little -t x4
	0000000 00005406 ffffffff
	0000010

Note that this was on a JUNO, BUT exactly the same steps can be used to
reach an SCMI Server living on a VM reachabke via virtio as long as the
system under test if properly configured to work with a virtio transport.

In a nutshell the exposed API is as follows:

/sys/kernel/debug/scmi_raw/
├── enable
├── errors
├── message
├── message_async
├── notification
├── transport_max_msg_size
├── transport_rx_timeout_ms
└── transport_tx_max_msg

where:

 - enable: used to enter/exit Raw mode...so as to exclude SCMI regular
   driver interference during testing (with some caveats...see below)
 - message*: used to send sync/async commands and read back immediate and
   delayed reponses (if any)
 - errors: used to report timeout and unexpected replies
 - notification: used to read any notification being spit by the system
   (if previously enabled by the user app)
 - transport*: a bunch of configuration useful to setup the user
   application expectations in terms of timeouts and message
   characteristics.

Each write corresponds to one command request and the replies or delayed
response are read back one message at time (receiving an EOF at each
message boundary).

The user application running the test is in charge of handling timeouts
and properly choosing SCMI sequence numbers for the outgoing requests: note
that the same fixed number can be re-used (...though discouraged...) as
long as the suite does NOT attempt to send multiple in-flight commands.

Since the SCMI core regular stack is partially used to deliver and collect
the messages, late replies after timeouts and any other sort of unexpected
message can be identified by the SCMI core as usual and it will be reported
under /errors for later analysis. (a userspace test-app will have anyway
properly detected the timeout on /message*)

All of the above has been roughly tested against a standard JUNO SCP SCMI
Server (mailbox trans) and an emulated SCMI Server living in a VM (virtio
trans) using a custom experimental version of the scmi-tests Compliance
suite patched to support Raw mode and posted at [2]. (still in development
...certainly not up for review as of now...it is just a mean for me to
test the testing API ... O_o)

The series is based on v6.0-rc1 and still an RFC since:

- the dynamic runtime enable/disable is still under development and has
  potential issues related to the dynamic unbinding/re-binding of SCMI
  drivers. (especially is not guaranteed on a complex systems to be able
  to return back to a fully functional normal state)

- needs a good deal of cleanup/split and additional docs/comments

- needs more complete testing, ideally running to completion at least the full
  SCMI compliance suite at [2] to use it as a reference

- the debugfs interface itself is up for discussion in this RFC indeed

Having said that (in such a concise and brief way :P) ...
	
...any feedback/comments are welcome !

Thanks,
Cristian


[1]: https://gitlab.arm.com/tests/scmi-tests
[2]: https://gitlab.arm.com/linux-arm/scmi-tests-cm/-/commits/raw_mode_support_V1/

Cristian Marussi (6):
  firmware: arm_scmi: Refactor xfer in-flight registration routines
  firmware: arm_scmi: Add bus helpers to enter raw mode
  firmware: arm_scmi: Add xfer raw helpers
  firmware: arm_scmi: Move errors defs and code to common.h
  firmware: arm_scmi: Add raw transmission support
  firmware: arm_scmi: Call Raw mode hooks from the core stack

 drivers/firmware/arm_scmi/Kconfig    |   13 +
 drivers/firmware/arm_scmi/Makefile   |    1 +
 drivers/firmware/arm_scmi/bus.c      |   69 +-
 drivers/firmware/arm_scmi/common.h   |   49 ++
 drivers/firmware/arm_scmi/driver.c   |  253 +++++--
 drivers/firmware/arm_scmi/raw_mode.c | 1046 ++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/raw_mode.h |   28 +
 include/linux/scmi_protocol.h        |    1 +
 8 files changed, 1385 insertions(+), 75 deletions(-)
 create mode 100644 drivers/firmware/arm_scmi/raw_mode.c
 create mode 100644 drivers/firmware/arm_scmi/raw_mode.h

-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 22+ messages in thread

* [RFC PATCH 0/6] Introduce a unified API for SCMI Server testing
@ 2022-08-16  7:24 ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Hi all,

This series aims to introduce a new SCMI unified userspace interface meant
to ease testing an SCMI Server implementation for compliance, fuzzing etc.,
from the perspective of the OSPM agent (non-secure world only ...)

It is proposed as a testing/development facility, it is NOT meant to be a
feature to use in production, but only enabled in Kconfig for test
deployments.

Currently an SCMI Compliance Suite like the one at [1] can only work by
injecting SCMI messages at the SCMI transport layer using the mailbox test
driver (CONFIG_MAILBOX_TEST) via its few debugfs entries and looking at
the related replies from the SCMI backend Server.

This approach has a few drawbacks:

- the SCMI Server under test MUST be reachable through a mailbox based
  SCMI transport: any other SCMI Server placement is not possible (like in
  a VM reachable via SCMI Virtio). In order to cover other placements in
  the current scenario we should write some sort of test driver for each
  and every existent SCMI transport and for any future additional transport
  ...this clearly does not scale.

- even in the mailbox case the userspace Compliance suite cannot simply
  send and receive bare SCMI messages BUT it has to properly lay them out
  into the shared memory exposed by the mailbox test driver as expected by
  the transport definitions. In other words such a userspace test
  application has to, not only use a proper transport driver for the system
  at hand, but it also has to have a comprehensive knowledge of the
  internals of the underlying transport in order to operate.

- last but not least, the system under test has to be specifically
  configured and built, in terms of Kconfig and DT, to perform such kind of
  testing, it cannot be used for anything else, which is unfortunate for
  CI/CD deployments.

This series introduces a new SCMI Raw mode support feature that, when
configured and enabled exposes a new interface in debugfs through which:

- a userspace application can inject bare SCMI binary messages into the
  SCMI core stack: such messages will be routed by the SCMI regular kernel
  stack to the backend Server using the currently configured transport
  transparently: in other words you can test the SCMI server no matter
  where it is placed, as long as it is reachable from the currently
  configured SCMI stack.
  Same goes the other way around on the reading path: any SCMI server reply
  can be read as a bare SCMI binary message from the same debugfs path.

- as a direct consequence of this way of injecting bare messages in the
  middle of the SCMI stack (instead of beneath it at the transport layer)
  the user application has to handle only bare SCMI messages without having
  to worry about the specific underlying transport internals that will be
  taken care of by the SCMI core stack itself using its own machinery,
  without duplicating such logic.

- last but not least, even when the SCMI Raw mode is enabled at compile
  time, the system under test DT configuration does NOT need any change and
  the mode itself is NOT anyway active until explicitly enabled at run-time
  via debugfs: as a consequence it does NOT interfere with the SCMI regular
  drivers stack until runtime enabled.
  You can boot a normal SCMI-based system and then switch to Raw mode only
  if you intend to run an SCMI Server oriented test suite (and possibly
  switch back to normal at the end....but here there are some issues/
  caveats on this resurrection path :D ...see down below)

A quick and trivial example from the shell...reading from a sensor
injecting a properly crafted packet in raw mode:

	# ENABLE RAW MODE...
	root@deb-buster-arm64:~# echo 1 > /sys/kernel/debug/scmi_raw/enable

	# INJECT THE SENSOR_READING MESSAGE FOR SENSOR ID=1 (binary little endian)
	root@deb-buster-arm64:~# echo -e -n \\x06\\x54\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00 > /sys/kernel/debug/scmi_raw/message

	# READING BACK THE REPLY...
	root@deb-buster-arm64:~# cat /sys/kernel/debug/scmi_raw/message | od --endian=little -t x4
	0000000 00005406 00000000 00000335 00000000
	0000020

while doing that, since Raw mode makes (partial) use of the regular SCMI
stack, you can observe the messages going through the SCMI stack with the
usual traces:

              bash-329     [000] ..... 14183.446808: scmi_msg_dump: pt=15 t=CMND msg_id=06 seq=0000 s=0 pyld=0100000000000000
   irq/35-mhu_db_l-81      [000] ..... 14183.447809: scmi_msg_dump: pt=15 t=RESP msg_id=06 seq=0000 s=0 pyld=3503000000000000


..trying to read in async when the backend server does NOT supports asyncs:

	# AN ASYNC SENSOR READING REQUEST...
	root@deb-buster-arm64:~# echo -e -n \\x06\\x54\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00 > /sys/kernel/debug/scmi_raw/message_async

              bash-329     [000] ..... 16415.938739: scmi_msg_dump: pt=15 t=CMND msg_id=06 seq=0000 s=0 pyld=0100000001000000
   irq/35-mhu_db_l-81      [000] ..... 16415.944129: scmi_msg_dump: pt=15 t=RESP msg_id=06 seq=0000 s=-1 pyld=

	# RETURNS A STATUS -1 FROM THE SERVER NOT SUPPORTING IT
	root@deb-buster-arm64:~# cat /sys/kernel/debug/scmi_raw/message | od --endian=little -t x4
	0000000 00005406 ffffffff
	0000010

Note that this was on a JUNO, BUT exactly the same steps can be used to
reach an SCMI Server living on a VM reachabke via virtio as long as the
system under test if properly configured to work with a virtio transport.

In a nutshell the exposed API is as follows:

/sys/kernel/debug/scmi_raw/
├── enable
├── errors
├── message
├── message_async
├── notification
├── transport_max_msg_size
├── transport_rx_timeout_ms
└── transport_tx_max_msg

where:

 - enable: used to enter/exit Raw mode...so as to exclude SCMI regular
   driver interference during testing (with some caveats...see below)
 - message*: used to send sync/async commands and read back immediate and
   delayed reponses (if any)
 - errors: used to report timeout and unexpected replies
 - notification: used to read any notification being spit by the system
   (if previously enabled by the user app)
 - transport*: a bunch of configuration useful to setup the user
   application expectations in terms of timeouts and message
   characteristics.

Each write corresponds to one command request and the replies or delayed
response are read back one message at time (receiving an EOF at each
message boundary).

The user application running the test is in charge of handling timeouts
and properly choosing SCMI sequence numbers for the outgoing requests: note
that the same fixed number can be re-used (...though discouraged...) as
long as the suite does NOT attempt to send multiple in-flight commands.

Since the SCMI core regular stack is partially used to deliver and collect
the messages, late replies after timeouts and any other sort of unexpected
message can be identified by the SCMI core as usual and it will be reported
under /errors for later analysis. (a userspace test-app will have anyway
properly detected the timeout on /message*)

All of the above has been roughly tested against a standard JUNO SCP SCMI
Server (mailbox trans) and an emulated SCMI Server living in a VM (virtio
trans) using a custom experimental version of the scmi-tests Compliance
suite patched to support Raw mode and posted at [2]. (still in development
...certainly not up for review as of now...it is just a mean for me to
test the testing API ... O_o)

The series is based on v6.0-rc1 and still an RFC since:

- the dynamic runtime enable/disable is still under development and has
  potential issues related to the dynamic unbinding/re-binding of SCMI
  drivers. (especially is not guaranteed on a complex systems to be able
  to return back to a fully functional normal state)

- needs a good deal of cleanup/split and additional docs/comments

- needs more complete testing, ideally running to completion at least the full
  SCMI compliance suite at [2] to use it as a reference

- the debugfs interface itself is up for discussion in this RFC indeed

Having said that (in such a concise and brief way :P) ...
	
...any feedback/comments are welcome !

Thanks,
Cristian


[1]: https://gitlab.arm.com/tests/scmi-tests
[2]: https://gitlab.arm.com/linux-arm/scmi-tests-cm/-/commits/raw_mode_support_V1/

Cristian Marussi (6):
  firmware: arm_scmi: Refactor xfer in-flight registration routines
  firmware: arm_scmi: Add bus helpers to enter raw mode
  firmware: arm_scmi: Add xfer raw helpers
  firmware: arm_scmi: Move errors defs and code to common.h
  firmware: arm_scmi: Add raw transmission support
  firmware: arm_scmi: Call Raw mode hooks from the core stack

 drivers/firmware/arm_scmi/Kconfig    |   13 +
 drivers/firmware/arm_scmi/Makefile   |    1 +
 drivers/firmware/arm_scmi/bus.c      |   69 +-
 drivers/firmware/arm_scmi/common.h   |   49 ++
 drivers/firmware/arm_scmi/driver.c   |  253 +++++--
 drivers/firmware/arm_scmi/raw_mode.c | 1046 ++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/raw_mode.h |   28 +
 include/linux/scmi_protocol.h        |    1 +
 8 files changed, 1385 insertions(+), 75 deletions(-)
 create mode 100644 drivers/firmware/arm_scmi/raw_mode.c
 create mode 100644 drivers/firmware/arm_scmi/raw_mode.h

-- 
2.32.0


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [RFC PATCH 1/6] firmware: arm_scmi: Refactor xfer in-flight registration routines
  2022-08-16  7:24 ` Cristian Marussi
@ 2022-08-16  7:24   ` Cristian Marussi
  -1 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Move the whole xfer in-flight registration process out of scmi_xfer_get
and while at that, split the sequence number selection steps from the
in-flight registration procedure itself.

No functional change.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 102 +++++++++++++++++++----------
 1 file changed, 68 insertions(+), 34 deletions(-)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 609ebedee9cb..e5193da2ce09 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -311,8 +311,6 @@ static int scmi_xfer_token_set(struct scmi_xfers_info *minfo,
 	if (xfer_id != next_token)
 		atomic_add((int)(xfer_id - next_token), &transfer_last_id);
 
-	/* Set in-flight */
-	set_bit(xfer_id, minfo->xfer_alloc_table);
 	xfer->hdr.seq = (u16)xfer_id;
 
 	return 0;
@@ -330,33 +328,77 @@ static inline void scmi_xfer_token_clear(struct scmi_xfers_info *minfo,
 	clear_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
 }
 
+/**
+ * scmi_xfer_inflight_register_unlocked  - Register the xfer as in-flight
+ *
+ * @xfer: The xfer to register
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Note that this helper assumes that the xfer to be registered as in-flight
+ * had been built using an xfer sequence number which still corresponds to a
+ * free slot in the xfer_alloc_table.
+ *
+ * Context: Assumes to be called with @xfer_lock already acquired.
+ */
+static inline void
+scmi_xfer_inflight_register_unlocked(struct scmi_xfer *xfer,
+				     struct scmi_xfers_info *minfo)
+{
+	/* Set in-flight */
+	set_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
+	hash_add(minfo->pending_xfers, &xfer->node, xfer->hdr.seq);
+	xfer->pending = true;
+}
+
+/**
+ * scmi_xfer_pending_set  - Pick a proper sequence number and mark the xfer
+ * as pending in-flight
+ *
+ * @xfer: The xfer to act upon
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Return: 0 on Success or error otherwise
+ */
+static inline int scmi_xfer_pending_set(struct scmi_xfer *xfer,
+					struct scmi_xfers_info *minfo)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&minfo->xfer_lock, flags);
+	/* Set a new monotonic token as the xfer sequence number */
+	ret = scmi_xfer_token_set(minfo, xfer);
+	if (!ret)
+		scmi_xfer_inflight_register_unlocked(xfer, minfo);
+	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
+	return ret;
+}
+
 /**
  * scmi_xfer_get() - Allocate one message
  *
  * @handle: Pointer to SCMI entity handle
  * @minfo: Pointer to Tx/Rx Message management info based on channel type
- * @set_pending: If true a monotonic token is picked and the xfer is added to
- *		 the pending hash table.
  *
  * Helper function which is used by various message functions that are
  * exposed to clients of this driver for allocating a message traffic event.
  *
- * Picks an xfer from the free list @free_xfers (if any available) and, if
- * required, sets a monotonically increasing token and stores the inflight xfer
- * into the @pending_xfers hashtable for later retrieval.
+ * Picks an xfer from the free list @free_xfers (if any available) and perform
+ * a basic initialization.
+ *
+ * Note that, at this point, still no sequence number is assigned to the
+ * allocated xfer, nor it is registered as a pending transaction.
  *
  * The successfully initialized xfer is refcounted.
  *
- * Context: Holds @xfer_lock while manipulating @xfer_alloc_table and
- *	    @free_xfers.
+ * Context: Holds @xfer_lock while manipulating @free_xfers.
  *
- * Return: 0 if all went fine, else corresponding error.
+ * Return: An initialized xfer if all went fine, else pointer error.
  */
 static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
-				       struct scmi_xfers_info *minfo,
-				       bool set_pending)
+				       struct scmi_xfers_info *minfo)
 {
-	int ret;
 	unsigned long flags;
 	struct scmi_xfer *xfer;
 
@@ -376,25 +418,8 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
 	 */
 	xfer->transfer_id = atomic_inc_return(&transfer_last_id);
 
-	if (set_pending) {
-		/* Pick and set monotonic token */
-		ret = scmi_xfer_token_set(minfo, xfer);
-		if (!ret) {
-			hash_add(minfo->pending_xfers, &xfer->node,
-				 xfer->hdr.seq);
-			xfer->pending = true;
-		} else {
-			dev_err(handle->dev,
-				"Failed to get monotonic token %d\n", ret);
-			hlist_add_head(&xfer->node, &minfo->free_xfers);
-			xfer = ERR_PTR(ret);
-		}
-	}
-
-	if (!IS_ERR(xfer)) {
-		refcount_set(&xfer->users, 1);
-		atomic_set(&xfer->busy, SCMI_XFER_FREE);
-	}
+	refcount_set(&xfer->users, 1);
+	atomic_set(&xfer->busy, SCMI_XFER_FREE);
 	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
 
 	return xfer;
@@ -652,7 +677,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
 	ktime_t ts;
 
 	ts = ktime_get_boottime();
-	xfer = scmi_xfer_get(cinfo->handle, minfo, false);
+	xfer = scmi_xfer_get(cinfo->handle, minfo);
 	if (IS_ERR(xfer)) {
 		dev_err(dev, "failed to get free message slot (%ld)\n",
 			PTR_ERR(xfer));
@@ -1041,13 +1066,22 @@ static int xfer_get_init(const struct scmi_protocol_handle *ph,
 	    tx_size > info->desc->max_msg_size)
 		return -ERANGE;
 
-	xfer = scmi_xfer_get(pi->handle, minfo, true);
+	xfer = scmi_xfer_get(pi->handle, minfo);
 	if (IS_ERR(xfer)) {
 		ret = PTR_ERR(xfer);
 		dev_err(dev, "failed to get free message slot(%d)\n", ret);
 		return ret;
 	}
 
+	/* Pick a sequence number and register this xfer as in-flight */
+	ret = scmi_xfer_pending_set(xfer, minfo);
+	if (ret) {
+		dev_err(pi->handle->dev,
+			"Failed to get monotonic token %d\n", ret);
+		__scmi_xfer_put(minfo, xfer);
+		return ret;
+	}
+
 	xfer->tx.len = tx_size;
 	xfer->rx.len = rx_size ? : info->desc->max_msg_size;
 	xfer->hdr.type = MSG_TYPE_COMMAND;
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 1/6] firmware: arm_scmi: Refactor xfer in-flight registration routines
@ 2022-08-16  7:24   ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Move the whole xfer in-flight registration process out of scmi_xfer_get
and while at that, split the sequence number selection steps from the
in-flight registration procedure itself.

No functional change.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 102 +++++++++++++++++++----------
 1 file changed, 68 insertions(+), 34 deletions(-)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 609ebedee9cb..e5193da2ce09 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -311,8 +311,6 @@ static int scmi_xfer_token_set(struct scmi_xfers_info *minfo,
 	if (xfer_id != next_token)
 		atomic_add((int)(xfer_id - next_token), &transfer_last_id);
 
-	/* Set in-flight */
-	set_bit(xfer_id, minfo->xfer_alloc_table);
 	xfer->hdr.seq = (u16)xfer_id;
 
 	return 0;
@@ -330,33 +328,77 @@ static inline void scmi_xfer_token_clear(struct scmi_xfers_info *minfo,
 	clear_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
 }
 
+/**
+ * scmi_xfer_inflight_register_unlocked  - Register the xfer as in-flight
+ *
+ * @xfer: The xfer to register
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Note that this helper assumes that the xfer to be registered as in-flight
+ * had been built using an xfer sequence number which still corresponds to a
+ * free slot in the xfer_alloc_table.
+ *
+ * Context: Assumes to be called with @xfer_lock already acquired.
+ */
+static inline void
+scmi_xfer_inflight_register_unlocked(struct scmi_xfer *xfer,
+				     struct scmi_xfers_info *minfo)
+{
+	/* Set in-flight */
+	set_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
+	hash_add(minfo->pending_xfers, &xfer->node, xfer->hdr.seq);
+	xfer->pending = true;
+}
+
+/**
+ * scmi_xfer_pending_set  - Pick a proper sequence number and mark the xfer
+ * as pending in-flight
+ *
+ * @xfer: The xfer to act upon
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Return: 0 on Success or error otherwise
+ */
+static inline int scmi_xfer_pending_set(struct scmi_xfer *xfer,
+					struct scmi_xfers_info *minfo)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&minfo->xfer_lock, flags);
+	/* Set a new monotonic token as the xfer sequence number */
+	ret = scmi_xfer_token_set(minfo, xfer);
+	if (!ret)
+		scmi_xfer_inflight_register_unlocked(xfer, minfo);
+	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
+	return ret;
+}
+
 /**
  * scmi_xfer_get() - Allocate one message
  *
  * @handle: Pointer to SCMI entity handle
  * @minfo: Pointer to Tx/Rx Message management info based on channel type
- * @set_pending: If true a monotonic token is picked and the xfer is added to
- *		 the pending hash table.
  *
  * Helper function which is used by various message functions that are
  * exposed to clients of this driver for allocating a message traffic event.
  *
- * Picks an xfer from the free list @free_xfers (if any available) and, if
- * required, sets a monotonically increasing token and stores the inflight xfer
- * into the @pending_xfers hashtable for later retrieval.
+ * Picks an xfer from the free list @free_xfers (if any available) and perform
+ * a basic initialization.
+ *
+ * Note that, at this point, still no sequence number is assigned to the
+ * allocated xfer, nor it is registered as a pending transaction.
  *
  * The successfully initialized xfer is refcounted.
  *
- * Context: Holds @xfer_lock while manipulating @xfer_alloc_table and
- *	    @free_xfers.
+ * Context: Holds @xfer_lock while manipulating @free_xfers.
  *
- * Return: 0 if all went fine, else corresponding error.
+ * Return: An initialized xfer if all went fine, else pointer error.
  */
 static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
-				       struct scmi_xfers_info *minfo,
-				       bool set_pending)
+				       struct scmi_xfers_info *minfo)
 {
-	int ret;
 	unsigned long flags;
 	struct scmi_xfer *xfer;
 
@@ -376,25 +418,8 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
 	 */
 	xfer->transfer_id = atomic_inc_return(&transfer_last_id);
 
-	if (set_pending) {
-		/* Pick and set monotonic token */
-		ret = scmi_xfer_token_set(minfo, xfer);
-		if (!ret) {
-			hash_add(minfo->pending_xfers, &xfer->node,
-				 xfer->hdr.seq);
-			xfer->pending = true;
-		} else {
-			dev_err(handle->dev,
-				"Failed to get monotonic token %d\n", ret);
-			hlist_add_head(&xfer->node, &minfo->free_xfers);
-			xfer = ERR_PTR(ret);
-		}
-	}
-
-	if (!IS_ERR(xfer)) {
-		refcount_set(&xfer->users, 1);
-		atomic_set(&xfer->busy, SCMI_XFER_FREE);
-	}
+	refcount_set(&xfer->users, 1);
+	atomic_set(&xfer->busy, SCMI_XFER_FREE);
 	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
 
 	return xfer;
@@ -652,7 +677,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
 	ktime_t ts;
 
 	ts = ktime_get_boottime();
-	xfer = scmi_xfer_get(cinfo->handle, minfo, false);
+	xfer = scmi_xfer_get(cinfo->handle, minfo);
 	if (IS_ERR(xfer)) {
 		dev_err(dev, "failed to get free message slot (%ld)\n",
 			PTR_ERR(xfer));
@@ -1041,13 +1066,22 @@ static int xfer_get_init(const struct scmi_protocol_handle *ph,
 	    tx_size > info->desc->max_msg_size)
 		return -ERANGE;
 
-	xfer = scmi_xfer_get(pi->handle, minfo, true);
+	xfer = scmi_xfer_get(pi->handle, minfo);
 	if (IS_ERR(xfer)) {
 		ret = PTR_ERR(xfer);
 		dev_err(dev, "failed to get free message slot(%d)\n", ret);
 		return ret;
 	}
 
+	/* Pick a sequence number and register this xfer as in-flight */
+	ret = scmi_xfer_pending_set(xfer, minfo);
+	if (ret) {
+		dev_err(pi->handle->dev,
+			"Failed to get monotonic token %d\n", ret);
+		__scmi_xfer_put(minfo, xfer);
+		return ret;
+	}
+
 	xfer->tx.len = tx_size;
 	xfer->rx.len = rx_size ? : info->desc->max_msg_size;
 	xfer->hdr.type = MSG_TYPE_COMMAND;
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 2/6] firmware: arm_scmi: Add bus helpers to enter raw mode
  2022-08-16  7:24 ` Cristian Marussi
@ 2022-08-16  7:24   ` Cristian Marussi
  -1 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Add a couple of helpers to be able to trigger the un-registration and the
re-registration of the whole stack of SCMI drivers from/to the SCMI bus.

Once this mode is enabled any new SCMI driver registration is inhibited
and delayed till the this new mode is disabled.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/bus.c    | 69 +++++++++++++++++++++++++++++-
 drivers/firmware/arm_scmi/common.h |  2 +
 include/linux/scmi_protocol.h      |  1 +
 3 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index d4e23101448a..c4b3371dcb1b 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -19,6 +19,10 @@ static DEFINE_IDA(scmi_bus_id);
 static DEFINE_IDR(scmi_protocols);
 static DEFINE_SPINLOCK(protocol_lock);
 
+static bool scmi_raw_mode_enabled;
+static LIST_HEAD(scmi_registered_drivers);
+static DEFINE_MUTEX(scmi_raw_mode_mtx);
+
 static const struct scmi_device_id *
 scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
 {
@@ -144,6 +148,18 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
 	driver->driver.owner = owner;
 	driver->driver.mod_name = mod_name;
 
+	mutex_lock(&scmi_raw_mode_mtx);
+	list_add_tail(&driver->node, &scmi_registered_drivers);
+	/*
+	 * If RAW mode is enabled here, the driver will be registered fully
+	 * only later, when RAW will be (if ever) disabled.
+	 */
+	if (scmi_raw_mode_enabled) {
+		mutex_unlock(&scmi_raw_mode_mtx);
+		return 0;
+	}
+	mutex_unlock(&scmi_raw_mode_mtx);
+
 	retval = driver_register(&driver->driver);
 	if (!retval)
 		pr_debug("registered new scmi driver %s\n", driver->name);
@@ -154,7 +170,18 @@ EXPORT_SYMBOL_GPL(scmi_driver_register);
 
 void scmi_driver_unregister(struct scmi_driver *driver)
 {
-	driver_unregister(&driver->driver);
+	mutex_lock(&scmi_raw_mode_mtx);
+	list_del_init(&driver->node);
+	/*
+	 * No need to unregister a driver when RAW mode is enabled, it would
+	 * have been already unregistered when RAW was enabled.
+	 */
+	if (!scmi_raw_mode_enabled) {
+		mutex_unlock(&scmi_raw_mode_mtx);
+		driver_unregister(&driver->driver);
+		mutex_lock(&scmi_raw_mode_mtx);
+	}
+	mutex_unlock(&scmi_raw_mode_mtx);
 	scmi_protocol_device_unrequest(driver->id_table);
 }
 EXPORT_SYMBOL_GPL(scmi_driver_unregister);
@@ -276,6 +303,46 @@ static void scmi_devices_unregister(void)
 	bus_for_each_dev(&scmi_bus_type, NULL, NULL, __scmi_devices_unregister);
 }
 
+void scmi_bus_raw_mode_enable(void)
+{
+	struct scmi_driver *driver;
+
+	/* On enable any known SCMI driver is unregistered */
+	mutex_lock(&scmi_raw_mode_mtx);
+	list_for_each_entry_reverse(driver, &scmi_registered_drivers, node) {
+		mutex_unlock(&scmi_raw_mode_mtx);
+
+		driver_unregister(&driver->driver);
+
+		mutex_lock(&scmi_raw_mode_mtx);
+	}
+	scmi_raw_mode_enabled = true;
+	mutex_unlock(&scmi_raw_mode_mtx);
+}
+
+void scmi_bus_raw_mode_disable(void)
+{
+	int retval;
+	struct scmi_driver *driver;
+
+	/*
+	 * On disable any known SCMI driver is registered again unless it was
+	 * removed already as a module.
+	 */
+	mutex_lock(&scmi_raw_mode_mtx);
+	list_for_each_entry(driver, &scmi_registered_drivers, node) {
+		mutex_unlock(&scmi_raw_mode_mtx);
+
+		retval = driver_register(&driver->driver);
+		if (retval)
+			pr_warn("Failed to rebind driver %s\n", driver->name);
+
+		mutex_lock(&scmi_raw_mode_mtx);
+	}
+	scmi_raw_mode_enabled = false;
+	mutex_unlock(&scmi_raw_mode_mtx);
+}
+
 int __init scmi_bus_init(void)
 {
 	int retval;
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 61aba7447c32..bbaac20544a5 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -171,6 +171,8 @@ int scmi_protocol_device_request(const struct scmi_device_id *id_table);
 void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table);
 struct scmi_device *scmi_child_dev_find(struct device *parent,
 					int prot_id, const char *name);
+void scmi_bus_raw_mode_enable(void);
+void scmi_bus_raw_mode_disable(void);
 
 /**
  * struct scmi_desc - Description of SoC integration
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 4f765bc788ff..39e64ec72984 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -820,6 +820,7 @@ struct scmi_driver {
 	void (*remove)(struct scmi_device *sdev);
 	const struct scmi_device_id *id_table;
 
+	struct list_head node;
 	struct device_driver driver;
 };
 
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 2/6] firmware: arm_scmi: Add bus helpers to enter raw mode
@ 2022-08-16  7:24   ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Add a couple of helpers to be able to trigger the un-registration and the
re-registration of the whole stack of SCMI drivers from/to the SCMI bus.

Once this mode is enabled any new SCMI driver registration is inhibited
and delayed till the this new mode is disabled.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/bus.c    | 69 +++++++++++++++++++++++++++++-
 drivers/firmware/arm_scmi/common.h |  2 +
 include/linux/scmi_protocol.h      |  1 +
 3 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
index d4e23101448a..c4b3371dcb1b 100644
--- a/drivers/firmware/arm_scmi/bus.c
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -19,6 +19,10 @@ static DEFINE_IDA(scmi_bus_id);
 static DEFINE_IDR(scmi_protocols);
 static DEFINE_SPINLOCK(protocol_lock);
 
+static bool scmi_raw_mode_enabled;
+static LIST_HEAD(scmi_registered_drivers);
+static DEFINE_MUTEX(scmi_raw_mode_mtx);
+
 static const struct scmi_device_id *
 scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
 {
@@ -144,6 +148,18 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
 	driver->driver.owner = owner;
 	driver->driver.mod_name = mod_name;
 
+	mutex_lock(&scmi_raw_mode_mtx);
+	list_add_tail(&driver->node, &scmi_registered_drivers);
+	/*
+	 * If RAW mode is enabled here, the driver will be registered fully
+	 * only later, when RAW will be (if ever) disabled.
+	 */
+	if (scmi_raw_mode_enabled) {
+		mutex_unlock(&scmi_raw_mode_mtx);
+		return 0;
+	}
+	mutex_unlock(&scmi_raw_mode_mtx);
+
 	retval = driver_register(&driver->driver);
 	if (!retval)
 		pr_debug("registered new scmi driver %s\n", driver->name);
@@ -154,7 +170,18 @@ EXPORT_SYMBOL_GPL(scmi_driver_register);
 
 void scmi_driver_unregister(struct scmi_driver *driver)
 {
-	driver_unregister(&driver->driver);
+	mutex_lock(&scmi_raw_mode_mtx);
+	list_del_init(&driver->node);
+	/*
+	 * No need to unregister a driver when RAW mode is enabled, it would
+	 * have been already unregistered when RAW was enabled.
+	 */
+	if (!scmi_raw_mode_enabled) {
+		mutex_unlock(&scmi_raw_mode_mtx);
+		driver_unregister(&driver->driver);
+		mutex_lock(&scmi_raw_mode_mtx);
+	}
+	mutex_unlock(&scmi_raw_mode_mtx);
 	scmi_protocol_device_unrequest(driver->id_table);
 }
 EXPORT_SYMBOL_GPL(scmi_driver_unregister);
@@ -276,6 +303,46 @@ static void scmi_devices_unregister(void)
 	bus_for_each_dev(&scmi_bus_type, NULL, NULL, __scmi_devices_unregister);
 }
 
+void scmi_bus_raw_mode_enable(void)
+{
+	struct scmi_driver *driver;
+
+	/* On enable any known SCMI driver is unregistered */
+	mutex_lock(&scmi_raw_mode_mtx);
+	list_for_each_entry_reverse(driver, &scmi_registered_drivers, node) {
+		mutex_unlock(&scmi_raw_mode_mtx);
+
+		driver_unregister(&driver->driver);
+
+		mutex_lock(&scmi_raw_mode_mtx);
+	}
+	scmi_raw_mode_enabled = true;
+	mutex_unlock(&scmi_raw_mode_mtx);
+}
+
+void scmi_bus_raw_mode_disable(void)
+{
+	int retval;
+	struct scmi_driver *driver;
+
+	/*
+	 * On disable any known SCMI driver is registered again unless it was
+	 * removed already as a module.
+	 */
+	mutex_lock(&scmi_raw_mode_mtx);
+	list_for_each_entry(driver, &scmi_registered_drivers, node) {
+		mutex_unlock(&scmi_raw_mode_mtx);
+
+		retval = driver_register(&driver->driver);
+		if (retval)
+			pr_warn("Failed to rebind driver %s\n", driver->name);
+
+		mutex_lock(&scmi_raw_mode_mtx);
+	}
+	scmi_raw_mode_enabled = false;
+	mutex_unlock(&scmi_raw_mode_mtx);
+}
+
 int __init scmi_bus_init(void)
 {
 	int retval;
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 61aba7447c32..bbaac20544a5 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -171,6 +171,8 @@ int scmi_protocol_device_request(const struct scmi_device_id *id_table);
 void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table);
 struct scmi_device *scmi_child_dev_find(struct device *parent,
 					int prot_id, const char *name);
+void scmi_bus_raw_mode_enable(void);
+void scmi_bus_raw_mode_disable(void);
 
 /**
  * struct scmi_desc - Description of SoC integration
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 4f765bc788ff..39e64ec72984 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -820,6 +820,7 @@ struct scmi_driver {
 	void (*remove)(struct scmi_device *sdev);
 	const struct scmi_device_id *id_table;
 
+	struct list_head node;
 	struct device_driver driver;
 };
 
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 3/6] firmware: arm_scmi: Add xfer raw helpers
  2022-08-16  7:24 ` Cristian Marussi
@ 2022-08-16  7:24   ` Cristian Marussi
  -1 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Add a few SCMI helpers useful to implement SCMI Raw access support.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/common.h |  7 +++
 drivers/firmware/arm_scmi/driver.c | 79 ++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+)

diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index bbaac20544a5..3316bf9eb98a 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -214,6 +214,13 @@ struct scmi_desc {
 	const bool atomic_enabled;
 };
 
+void scmi_xfer_raw_put(const struct scmi_handle *handle,
+		       struct scmi_xfer *xfer);
+struct scmi_xfer *scmi_xfer_raw_get(const struct scmi_handle *handle);
+
+int scmi_xfer_raw_inflight_register(const struct scmi_handle *handle,
+				    struct scmi_xfer *xfer);
+
 #ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
 extern const struct scmi_desc scmi_mailbox_desc;
 #endif
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index e5193da2ce09..797826237776 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -350,6 +350,53 @@ scmi_xfer_inflight_register_unlocked(struct scmi_xfer *xfer,
 	xfer->pending = true;
 }
 
+/**
+ * scmi_xfer_inflight_register  - Try to register an xfer as in-flight
+ *
+ * @xfer: The xfer to register
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Note that this helper does NOT assume anything about the sequence number
+ * that was baked into the provided xfer, so it checks at first if it can
+ * be mapped to a free slot and fails with an error if another xfer with the
+ * same sequence number is currently still registered as in-flight.
+ *
+ * Return: 0 on Success or -EBUSY if sequence number embedded in the xfer
+ *	   could not rbe mapped to a free slot in the xfer_alloc_table.
+ */
+static int scmi_xfer_inflight_register(struct scmi_xfer *xfer,
+				       struct scmi_xfers_info *minfo)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&minfo->xfer_lock, flags);
+	if (!test_bit(xfer->hdr.seq, minfo->xfer_alloc_table))
+		scmi_xfer_inflight_register_unlocked(xfer, minfo);
+	else
+		ret = -EBUSY;
+	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
+	return ret;
+}
+
+/**
+ * scmi_xfer_raw_inflight_register  - An helper to register the given xfer as in
+ * flight on the TX channe, if possible.
+ *
+ * @handle: Pointer to SCMI entity handle
+ * @xfer: The xfer to register
+ *
+ * Return: 0 on Success, error otherwise
+ */
+int scmi_xfer_raw_inflight_register(const struct scmi_handle *handle,
+				    struct scmi_xfer *xfer)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	return scmi_xfer_inflight_register(xfer, &info->tx_minfo);
+}
+
 /**
  * scmi_xfer_pending_set  - Pick a proper sequence number and mark the xfer
  * as pending in-flight
@@ -425,6 +472,22 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
 	return xfer;
 }
 
+/**
+ * scmi_xfer_raw_get  - Helper to get a bare free xfer from the TX channel
+ *
+ * @handle: Pointer to SCMI entity handle
+ *
+ * Note that xfer is taken from the TX channel structures.
+ *
+ * Return: A valid xfer on Success, or an error-pointer otherwise
+ */
+struct scmi_xfer *scmi_xfer_raw_get(const struct scmi_handle *handle)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	return scmi_xfer_get(handle, &info->tx_minfo);
+}
+
 /**
  * __scmi_xfer_put() - Release a message
  *
@@ -453,6 +516,22 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
 	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
 }
 
+/**
+ * scmi_xfer_raw_put  - Release an xfer that was taken by @scmi_xfer_raw_get
+ *
+ * @handle: Pointer to SCMI entity handle
+ * @xfer: A reference to the xfer to put
+ *
+ * Note that as with other xfer_put() handlers the xfer is really effectively
+ * released only if there are no more users on the system.
+ */
+void scmi_xfer_raw_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	return __scmi_xfer_put(&info->tx_minfo, xfer);
+}
+
 /**
  * scmi_xfer_lookup_unlocked  -  Helper to lookup an xfer_id
  *
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 3/6] firmware: arm_scmi: Add xfer raw helpers
@ 2022-08-16  7:24   ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Add a few SCMI helpers useful to implement SCMI Raw access support.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/common.h |  7 +++
 drivers/firmware/arm_scmi/driver.c | 79 ++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+)

diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index bbaac20544a5..3316bf9eb98a 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -214,6 +214,13 @@ struct scmi_desc {
 	const bool atomic_enabled;
 };
 
+void scmi_xfer_raw_put(const struct scmi_handle *handle,
+		       struct scmi_xfer *xfer);
+struct scmi_xfer *scmi_xfer_raw_get(const struct scmi_handle *handle);
+
+int scmi_xfer_raw_inflight_register(const struct scmi_handle *handle,
+				    struct scmi_xfer *xfer);
+
 #ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
 extern const struct scmi_desc scmi_mailbox_desc;
 #endif
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index e5193da2ce09..797826237776 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -350,6 +350,53 @@ scmi_xfer_inflight_register_unlocked(struct scmi_xfer *xfer,
 	xfer->pending = true;
 }
 
+/**
+ * scmi_xfer_inflight_register  - Try to register an xfer as in-flight
+ *
+ * @xfer: The xfer to register
+ * @minfo: Pointer to Tx/Rx Message management info based on channel type
+ *
+ * Note that this helper does NOT assume anything about the sequence number
+ * that was baked into the provided xfer, so it checks at first if it can
+ * be mapped to a free slot and fails with an error if another xfer with the
+ * same sequence number is currently still registered as in-flight.
+ *
+ * Return: 0 on Success or -EBUSY if sequence number embedded in the xfer
+ *	   could not rbe mapped to a free slot in the xfer_alloc_table.
+ */
+static int scmi_xfer_inflight_register(struct scmi_xfer *xfer,
+				       struct scmi_xfers_info *minfo)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&minfo->xfer_lock, flags);
+	if (!test_bit(xfer->hdr.seq, minfo->xfer_alloc_table))
+		scmi_xfer_inflight_register_unlocked(xfer, minfo);
+	else
+		ret = -EBUSY;
+	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
+
+	return ret;
+}
+
+/**
+ * scmi_xfer_raw_inflight_register  - An helper to register the given xfer as in
+ * flight on the TX channe, if possible.
+ *
+ * @handle: Pointer to SCMI entity handle
+ * @xfer: The xfer to register
+ *
+ * Return: 0 on Success, error otherwise
+ */
+int scmi_xfer_raw_inflight_register(const struct scmi_handle *handle,
+				    struct scmi_xfer *xfer)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	return scmi_xfer_inflight_register(xfer, &info->tx_minfo);
+}
+
 /**
  * scmi_xfer_pending_set  - Pick a proper sequence number and mark the xfer
  * as pending in-flight
@@ -425,6 +472,22 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle,
 	return xfer;
 }
 
+/**
+ * scmi_xfer_raw_get  - Helper to get a bare free xfer from the TX channel
+ *
+ * @handle: Pointer to SCMI entity handle
+ *
+ * Note that xfer is taken from the TX channel structures.
+ *
+ * Return: A valid xfer on Success, or an error-pointer otherwise
+ */
+struct scmi_xfer *scmi_xfer_raw_get(const struct scmi_handle *handle)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	return scmi_xfer_get(handle, &info->tx_minfo);
+}
+
 /**
  * __scmi_xfer_put() - Release a message
  *
@@ -453,6 +516,22 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
 	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
 }
 
+/**
+ * scmi_xfer_raw_put  - Release an xfer that was taken by @scmi_xfer_raw_get
+ *
+ * @handle: Pointer to SCMI entity handle
+ * @xfer: A reference to the xfer to put
+ *
+ * Note that as with other xfer_put() handlers the xfer is really effectively
+ * released only if there are no more users on the system.
+ */
+void scmi_xfer_raw_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
+{
+	struct scmi_info *info = handle_to_scmi_info(handle);
+
+	return __scmi_xfer_put(&info->tx_minfo, xfer);
+}
+
 /**
  * scmi_xfer_lookup_unlocked  -  Helper to lookup an xfer_id
  *
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 4/6] firmware: arm_scmi: Move errors defs and code to common.h
  2022-08-16  7:24 ` Cristian Marussi
@ 2022-08-16  7:24   ` Cristian Marussi
  -1 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Move SCMI error codes definitions and helper to the common.h header
together with the delayed response timeout define.

No functional change.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/common.h | 40 ++++++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/driver.c | 40 ------------------------------
 2 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 3316bf9eb98a..5ed64bfe9054 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -27,6 +27,46 @@
 #include "protocols.h"
 #include "notify.h"
 
+#define SCMI_MAX_RESPONSE_TIMEOUT	(2 * MSEC_PER_SEC)
+
+enum scmi_error_codes {
+	SCMI_SUCCESS = 0,	/* Success */
+	SCMI_ERR_SUPPORT = -1,	/* Not supported */
+	SCMI_ERR_PARAMS = -2,	/* Invalid Parameters */
+	SCMI_ERR_ACCESS = -3,	/* Invalid access/permission denied */
+	SCMI_ERR_ENTRY = -4,	/* Not found */
+	SCMI_ERR_RANGE = -5,	/* Value out of range */
+	SCMI_ERR_BUSY = -6,	/* Device busy */
+	SCMI_ERR_COMMS = -7,	/* Communication Error */
+	SCMI_ERR_GENERIC = -8,	/* Generic Error */
+	SCMI_ERR_HARDWARE = -9,	/* Hardware Error */
+	SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
+};
+
+static const int scmi_linux_errmap[] = {
+	/* better than switch case as long as return value is continuous */
+	0,			/* SCMI_SUCCESS */
+	-EOPNOTSUPP,		/* SCMI_ERR_SUPPORT */
+	-EINVAL,		/* SCMI_ERR_PARAM */
+	-EACCES,		/* SCMI_ERR_ACCESS */
+	-ENOENT,		/* SCMI_ERR_ENTRY */
+	-ERANGE,		/* SCMI_ERR_RANGE */
+	-EBUSY,			/* SCMI_ERR_BUSY */
+	-ECOMM,			/* SCMI_ERR_COMMS */
+	-EIO,			/* SCMI_ERR_GENERIC */
+	-EREMOTEIO,		/* SCMI_ERR_HARDWARE */
+	-EPROTO,		/* SCMI_ERR_PROTOCOL */
+};
+
+static inline int scmi_to_linux_errno(int errno)
+{
+	int err_idx = -errno;
+
+	if (err_idx >= SCMI_SUCCESS && err_idx < ARRAY_SIZE(scmi_linux_errmap))
+		return scmi_linux_errmap[err_idx];
+	return -EIO;
+}
+
 #define MSG_ID_MASK		GENMASK(7, 0)
 #define MSG_XTRACT_ID(hdr)	FIELD_GET(MSG_ID_MASK, (hdr))
 #define MSG_TYPE_MASK		GENMASK(9, 8)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 797826237776..141a140792a5 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -37,20 +37,6 @@
 #define CREATE_TRACE_POINTS
 #include <trace/events/scmi.h>
 
-enum scmi_error_codes {
-	SCMI_SUCCESS = 0,	/* Success */
-	SCMI_ERR_SUPPORT = -1,	/* Not supported */
-	SCMI_ERR_PARAMS = -2,	/* Invalid Parameters */
-	SCMI_ERR_ACCESS = -3,	/* Invalid access/permission denied */
-	SCMI_ERR_ENTRY = -4,	/* Not found */
-	SCMI_ERR_RANGE = -5,	/* Value out of range */
-	SCMI_ERR_BUSY = -6,	/* Device busy */
-	SCMI_ERR_COMMS = -7,	/* Communication Error */
-	SCMI_ERR_GENERIC = -8,	/* Generic Error */
-	SCMI_ERR_HARDWARE = -9,	/* Hardware Error */
-	SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
-};
-
 /* List of all SCMI devices active in system */
 static LIST_HEAD(scmi_list);
 /* Protection for the entire list */
@@ -170,30 +156,6 @@ struct scmi_info {
 
 #define handle_to_scmi_info(h)	container_of(h, struct scmi_info, handle)
 
-static const int scmi_linux_errmap[] = {
-	/* better than switch case as long as return value is continuous */
-	0,			/* SCMI_SUCCESS */
-	-EOPNOTSUPP,		/* SCMI_ERR_SUPPORT */
-	-EINVAL,		/* SCMI_ERR_PARAM */
-	-EACCES,		/* SCMI_ERR_ACCESS */
-	-ENOENT,		/* SCMI_ERR_ENTRY */
-	-ERANGE,		/* SCMI_ERR_RANGE */
-	-EBUSY,			/* SCMI_ERR_BUSY */
-	-ECOMM,			/* SCMI_ERR_COMMS */
-	-EIO,			/* SCMI_ERR_GENERIC */
-	-EREMOTEIO,		/* SCMI_ERR_HARDWARE */
-	-EPROTO,		/* SCMI_ERR_PROTOCOL */
-};
-
-static inline int scmi_to_linux_errno(int errno)
-{
-	int err_idx = -errno;
-
-	if (err_idx >= SCMI_SUCCESS && err_idx < ARRAY_SIZE(scmi_linux_errmap))
-		return scmi_linux_errmap[err_idx];
-	return -EIO;
-}
-
 void scmi_notification_instance_data_set(const struct scmi_handle *handle,
 					 void *priv)
 {
@@ -1056,8 +1018,6 @@ static void reset_rx_to_maxsz(const struct scmi_protocol_handle *ph,
 	xfer->rx.len = info->desc->max_msg_size;
 }
 
-#define SCMI_MAX_RESPONSE_TIMEOUT	(2 * MSEC_PER_SEC)
-
 /**
  * do_xfer_with_response() - Do one transfer and wait until the delayed
  *	response is received
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 4/6] firmware: arm_scmi: Move errors defs and code to common.h
@ 2022-08-16  7:24   ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Move SCMI error codes definitions and helper to the common.h header
together with the delayed response timeout define.

No functional change.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/common.h | 40 ++++++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/driver.c | 40 ------------------------------
 2 files changed, 40 insertions(+), 40 deletions(-)

diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 3316bf9eb98a..5ed64bfe9054 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -27,6 +27,46 @@
 #include "protocols.h"
 #include "notify.h"
 
+#define SCMI_MAX_RESPONSE_TIMEOUT	(2 * MSEC_PER_SEC)
+
+enum scmi_error_codes {
+	SCMI_SUCCESS = 0,	/* Success */
+	SCMI_ERR_SUPPORT = -1,	/* Not supported */
+	SCMI_ERR_PARAMS = -2,	/* Invalid Parameters */
+	SCMI_ERR_ACCESS = -3,	/* Invalid access/permission denied */
+	SCMI_ERR_ENTRY = -4,	/* Not found */
+	SCMI_ERR_RANGE = -5,	/* Value out of range */
+	SCMI_ERR_BUSY = -6,	/* Device busy */
+	SCMI_ERR_COMMS = -7,	/* Communication Error */
+	SCMI_ERR_GENERIC = -8,	/* Generic Error */
+	SCMI_ERR_HARDWARE = -9,	/* Hardware Error */
+	SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
+};
+
+static const int scmi_linux_errmap[] = {
+	/* better than switch case as long as return value is continuous */
+	0,			/* SCMI_SUCCESS */
+	-EOPNOTSUPP,		/* SCMI_ERR_SUPPORT */
+	-EINVAL,		/* SCMI_ERR_PARAM */
+	-EACCES,		/* SCMI_ERR_ACCESS */
+	-ENOENT,		/* SCMI_ERR_ENTRY */
+	-ERANGE,		/* SCMI_ERR_RANGE */
+	-EBUSY,			/* SCMI_ERR_BUSY */
+	-ECOMM,			/* SCMI_ERR_COMMS */
+	-EIO,			/* SCMI_ERR_GENERIC */
+	-EREMOTEIO,		/* SCMI_ERR_HARDWARE */
+	-EPROTO,		/* SCMI_ERR_PROTOCOL */
+};
+
+static inline int scmi_to_linux_errno(int errno)
+{
+	int err_idx = -errno;
+
+	if (err_idx >= SCMI_SUCCESS && err_idx < ARRAY_SIZE(scmi_linux_errmap))
+		return scmi_linux_errmap[err_idx];
+	return -EIO;
+}
+
 #define MSG_ID_MASK		GENMASK(7, 0)
 #define MSG_XTRACT_ID(hdr)	FIELD_GET(MSG_ID_MASK, (hdr))
 #define MSG_TYPE_MASK		GENMASK(9, 8)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 797826237776..141a140792a5 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -37,20 +37,6 @@
 #define CREATE_TRACE_POINTS
 #include <trace/events/scmi.h>
 
-enum scmi_error_codes {
-	SCMI_SUCCESS = 0,	/* Success */
-	SCMI_ERR_SUPPORT = -1,	/* Not supported */
-	SCMI_ERR_PARAMS = -2,	/* Invalid Parameters */
-	SCMI_ERR_ACCESS = -3,	/* Invalid access/permission denied */
-	SCMI_ERR_ENTRY = -4,	/* Not found */
-	SCMI_ERR_RANGE = -5,	/* Value out of range */
-	SCMI_ERR_BUSY = -6,	/* Device busy */
-	SCMI_ERR_COMMS = -7,	/* Communication Error */
-	SCMI_ERR_GENERIC = -8,	/* Generic Error */
-	SCMI_ERR_HARDWARE = -9,	/* Hardware Error */
-	SCMI_ERR_PROTOCOL = -10,/* Protocol Error */
-};
-
 /* List of all SCMI devices active in system */
 static LIST_HEAD(scmi_list);
 /* Protection for the entire list */
@@ -170,30 +156,6 @@ struct scmi_info {
 
 #define handle_to_scmi_info(h)	container_of(h, struct scmi_info, handle)
 
-static const int scmi_linux_errmap[] = {
-	/* better than switch case as long as return value is continuous */
-	0,			/* SCMI_SUCCESS */
-	-EOPNOTSUPP,		/* SCMI_ERR_SUPPORT */
-	-EINVAL,		/* SCMI_ERR_PARAM */
-	-EACCES,		/* SCMI_ERR_ACCESS */
-	-ENOENT,		/* SCMI_ERR_ENTRY */
-	-ERANGE,		/* SCMI_ERR_RANGE */
-	-EBUSY,			/* SCMI_ERR_BUSY */
-	-ECOMM,			/* SCMI_ERR_COMMS */
-	-EIO,			/* SCMI_ERR_GENERIC */
-	-EREMOTEIO,		/* SCMI_ERR_HARDWARE */
-	-EPROTO,		/* SCMI_ERR_PROTOCOL */
-};
-
-static inline int scmi_to_linux_errno(int errno)
-{
-	int err_idx = -errno;
-
-	if (err_idx >= SCMI_SUCCESS && err_idx < ARRAY_SIZE(scmi_linux_errmap))
-		return scmi_linux_errmap[err_idx];
-	return -EIO;
-}
-
 void scmi_notification_instance_data_set(const struct scmi_handle *handle,
 					 void *priv)
 {
@@ -1056,8 +1018,6 @@ static void reset_rx_to_maxsz(const struct scmi_protocol_handle *ph,
 	xfer->rx.len = info->desc->max_msg_size;
 }
 
-#define SCMI_MAX_RESPONSE_TIMEOUT	(2 * MSEC_PER_SEC)
-
 /**
  * do_xfer_with_response() - Do one transfer and wait until the delayed
  *	response is received
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
  2022-08-16  7:24 ` Cristian Marussi
@ 2022-08-16  7:24   ` Cristian Marussi
  -1 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Add SCMI Raw mode support which exposes a userspace interface rooted under
/sys/kernel/debug/scmi_raw.

Raw mode can be enabled/disabled at runtime via ./scmi_raw/enable.
Once enabled, all the regular SCMI drivers activity is inhibited and a
userspace application can then inject and read back bare SCMI messages
writing and reading to/from ./scmi_raw/message* entries.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/Kconfig    |   13 +
 drivers/firmware/arm_scmi/Makefile   |    1 +
 drivers/firmware/arm_scmi/raw_mode.c | 1046 ++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/raw_mode.h |   28 +
 4 files changed, 1088 insertions(+)
 create mode 100644 drivers/firmware/arm_scmi/raw_mode.c
 create mode 100644 drivers/firmware/arm_scmi/raw_mode.h

diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig
index a14f65444b35..ab726a92ac2f 100644
--- a/drivers/firmware/arm_scmi/Kconfig
+++ b/drivers/firmware/arm_scmi/Kconfig
@@ -23,6 +23,19 @@ config ARM_SCMI_PROTOCOL
 
 if ARM_SCMI_PROTOCOL
 
+config ARM_SCMI_RAW_MODE_SUPPORT
+	bool "Enable support for SCMI Raw transmission mode"
+	help
+	  Enable support for SCMI Raw transmission mode.
+
+	  If enabled allows the direct injection and snooping of SCMI bare
+	  messages through a dedicated debugfs interface.
+	  It is meant to be used by SCMI compliance/testing suites.
+
+	  When enabled regular SCMI drivers interactions are inhibited in
+	  order to avoid unexpected interactions with the SCMI Raw message
+	  flow. If unsure say N.
+
 config ARM_SCMI_HAVE_TRANSPORT
 	bool
 	help
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 9ea86f8cc8f7..7c1aca60c8ce 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 scmi-bus-y = bus.o
 scmi-driver-y = driver.o notify.o
+scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o
 scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
 scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += mailbox.o
 scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
diff --git a/drivers/firmware/arm_scmi/raw_mode.c b/drivers/firmware/arm_scmi/raw_mode.c
new file mode 100644
index 000000000000..c14cdd12b140
--- /dev/null
+++ b/drivers/firmware/arm_scmi/raw_mode.c
@@ -0,0 +1,1046 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Raw mode support
+ *
+ * Copyright (C) 2022 ARM Ltd.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+
+#include "common.h"
+
+#include "raw_mode.h"
+
+#include <trace/events/scmi.h>
+
+#define SCMI_XFER_RAW_MAX_RETRIES	10
+
+struct scmi_xfer_raw_work {
+	unsigned long max_tmo;
+	struct scmi_raw_mode_info *raw;
+	struct list_head free_waiters;
+	/* Protect free_waiters list */
+	struct mutex free_mtx;
+	struct list_head active_waiters;
+	/* Protect active_waiters list */
+	struct mutex active_mtx;
+	wait_queue_head_t waiters_wq;
+	struct work_struct waiters_work;
+	bool wait_wq_shutdown;
+	struct workqueue_struct	*wait_wq;
+};
+
+struct scmi_raw_mode_info {
+	bool enabled;
+	struct scmi_chan_info *cinfo;
+	const struct scmi_desc *desc;
+	int tx_max_msg;
+	struct list_head free_raw_bufs[SCMI_RAW_MAX_QUEUE];
+	/* Protect free_raw_bufs[] lists */
+	spinlock_t free_bufs_lock[SCMI_RAW_MAX_QUEUE];
+	struct list_head msg_q[SCMI_RAW_MAX_QUEUE];
+	/* Protect msg_q[] lists */
+	spinlock_t msg_q_lock[SCMI_RAW_MAX_QUEUE];
+	wait_queue_head_t wqs[SCMI_RAW_MAX_QUEUE];
+	struct scmi_xfer_raw_work *wrk;
+	struct dentry *dentry;
+	void *gid;
+};
+
+struct scmi_xfer_raw_waiter {
+	unsigned long start_jiffies;
+	struct scmi_xfer *xfer;
+	struct completion async_response;
+	struct list_head node;
+};
+
+struct scmi_raw_buffer {
+	size_t max_len;
+	struct scmi_msg msg;
+	struct list_head node;
+};
+
+struct scmi_raw_data {
+	struct scmi_raw_mode_info *raw;
+	struct scmi_msg tx;
+	size_t tx_size;
+	size_t tx_req;
+	struct scmi_msg rx;
+	size_t rx_size;
+};
+
+static inline
+struct scmi_raw_buffer *scmi_raw_buffer_get(struct scmi_raw_mode_info *raw,
+					    unsigned int idx)
+{
+	unsigned long flags;
+	struct scmi_raw_buffer *rb = NULL;
+	struct list_head *head = &raw->free_raw_bufs[idx];
+
+	spin_lock_irqsave(&raw->free_bufs_lock[idx], flags);
+	if (!list_empty(head)) {
+		rb = list_first_entry(head, struct scmi_raw_buffer, node);
+		list_del_init(&rb->node);
+	}
+	spin_unlock_irqrestore(&raw->free_bufs_lock[idx], flags);
+
+	return rb;
+}
+
+static inline void scmi_raw_buffer_put(struct scmi_raw_mode_info *raw,
+				       struct scmi_raw_buffer *rb,
+				       unsigned int idx)
+{
+	unsigned long flags;
+
+	rb->msg.len = rb->max_len;
+
+	spin_lock_irqsave(&raw->free_bufs_lock[idx], flags);
+	list_add_tail(&rb->node, &raw->free_raw_bufs[idx]);
+	spin_unlock_irqrestore(&raw->free_bufs_lock[idx], flags);
+}
+
+static inline void scmi_raw_buffer_enqueue(struct scmi_raw_mode_info *raw,
+					   struct scmi_raw_buffer *rb,
+					   unsigned int idx)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&raw->msg_q_lock[idx], flags);
+	list_add_tail(&rb->node, &raw->msg_q[idx]);
+	spin_unlock_irqrestore(&raw->msg_q_lock[idx], flags);
+
+	wake_up_interruptible(&raw->wqs[idx]);
+}
+
+static inline struct scmi_raw_buffer*
+scmi_raw_buffer_dequeue(struct scmi_raw_mode_info *raw, unsigned int idx)
+{
+	unsigned long flags;
+	struct scmi_raw_buffer *rb = NULL;
+
+	spin_lock_irqsave(&raw->msg_q_lock[idx], flags);
+	if (!list_empty(&raw->msg_q[idx])) {
+		rb = list_first_entry(&raw->msg_q[idx],
+				      struct scmi_raw_buffer, node);
+		list_del_init(&rb->node);
+	}
+	spin_unlock_irqrestore(&raw->msg_q_lock[idx], flags);
+
+	return rb;
+}
+
+static void scmi_raw_buffer_queue_flush(struct scmi_raw_mode_info *raw,
+					unsigned int idx)
+{
+	struct scmi_raw_buffer *rb;
+
+	do {
+		rb = scmi_raw_buffer_dequeue(raw, idx);
+		if (rb)
+			scmi_raw_buffer_put(raw, rb, idx);
+	} while (rb);
+}
+
+static inline struct scmi_xfer_raw_waiter *
+scmi_xfer_raw_waiter_get(struct scmi_xfer_raw_work *wrk, struct scmi_xfer *xfer,
+			 bool async)
+{
+	struct scmi_xfer_raw_waiter *rw = NULL;
+
+	mutex_lock(&wrk->free_mtx);
+	if (!list_empty(&wrk->free_waiters)) {
+		rw = list_first_entry(&wrk->free_waiters,
+				      struct scmi_xfer_raw_waiter, node);
+		list_del_init(&rw->node);
+
+		if (async) {
+			reinit_completion(&rw->async_response);
+			xfer->async_done = &rw->async_response;
+		}
+
+		rw->xfer = xfer;
+	}
+	//TODO this critS can be shortened...
+	mutex_unlock(&wrk->free_mtx);
+
+	return rw;
+}
+
+static inline
+void scmi_xfer_raw_waiter_put(struct scmi_xfer_raw_work *wrk,
+			      struct scmi_xfer_raw_waiter *rw)
+{
+	if (rw->xfer) {
+		rw->xfer->async_done = NULL;
+		rw->xfer = NULL;
+	}
+
+	mutex_lock(&wrk->free_mtx);
+	list_add_tail(&rw->node, &wrk->free_waiters);
+	mutex_unlock(&wrk->free_mtx);
+}
+
+static inline
+void scmi_xfer_raw_waiter_enqueue(struct scmi_xfer_raw_work *wrk,
+				  struct scmi_xfer_raw_waiter *rw)
+{
+	rw->start_jiffies = jiffies;
+
+	trace_scmi_xfer_response_wait(rw->xfer->transfer_id, rw->xfer->hdr.id,
+				      rw->xfer->hdr.protocol_id,
+				      rw->xfer->hdr.seq,
+				      wrk->raw->desc->max_rx_timeout_ms,
+				      rw->xfer->hdr.poll_completion);
+
+	mutex_lock(&wrk->active_mtx);
+	list_add_tail(&rw->node, &wrk->active_waiters);
+	mutex_unlock(&wrk->active_mtx);
+
+	wake_up_interruptible(&wrk->waiters_wq);
+}
+
+static struct scmi_xfer_raw_waiter*
+scmi_xfer_raw_waiter_dequeue(struct scmi_xfer_raw_work *wrk)
+{
+	struct scmi_xfer_raw_waiter *rw;
+
+	mutex_lock(&wrk->active_mtx);
+	while (list_empty(&wrk->active_waiters)) {
+		int ret;
+
+		mutex_unlock(&wrk->active_mtx);
+
+		ret = wait_event_interruptible(wrk->waiters_wq,
+					       !list_empty(&wrk->active_waiters) ||
+					       wrk->wait_wq_shutdown);
+		if (ret || wrk->wait_wq_shutdown)
+			return NULL;
+
+		mutex_lock(&wrk->active_mtx);
+	}
+
+	rw = list_first_entry(&wrk->active_waiters,
+			      struct scmi_xfer_raw_waiter, node);
+	list_del_init(&rw->node);
+	mutex_unlock(&wrk->active_mtx);
+
+	return rw;
+}
+
+static void scmi_xfer_raw_worker(struct work_struct *work)
+{
+	struct scmi_xfer_raw_work *wrk;
+	struct scmi_raw_mode_info *raw;
+	struct scmi_chan_info *cinfo;
+	struct device *dev;
+
+	wrk = container_of(work, struct scmi_xfer_raw_work, waiters_work);
+
+	raw = wrk->raw;
+	cinfo = raw->cinfo;
+	dev = raw->cinfo->handle->dev;
+
+	do {
+		int ret = 0;
+		unsigned long aging, tmo;
+		struct scmi_xfer *xfer;
+		struct scmi_xfer_raw_waiter *rw;
+
+		/*
+		 * Waiters are queued by wait-deadline at the end, so some of
+		 * them could have been already expired when processed, BUT we
+		 * have to check the completion status anyway just in case a
+		 * virtually expired (aged) transaction was indeed completed
+		 * fine and we'll have to wait for the asynchronous part (if
+		 * any).
+		 */
+		rw = scmi_xfer_raw_waiter_dequeue(wrk);
+		if (!rw)
+			return;
+
+		xfer = rw->xfer;
+
+		aging = jiffies - rw->start_jiffies;
+		tmo = wrk->max_tmo > aging ? wrk->max_tmo - aging : 0;
+
+		if ((tmo && !wait_for_completion_timeout(&xfer->done, tmo)) ||
+		    (!tmo && !try_wait_for_completion(&xfer->done))) {
+			dev_err(dev, "timed out in RAW response - HDR:%08X\n",
+				pack_scmi_header(&xfer->hdr));
+			ret = -ETIMEDOUT;
+		}
+
+		/* Avoid unneeded async waits */
+		if (!ret && xfer->hdr.status)
+			ret = scmi_to_linux_errno(xfer->hdr.status);
+
+		if (raw->desc->ops->mark_txdone)
+			raw->desc->ops->mark_txdone(cinfo, ret, xfer);
+
+		trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
+				    xfer->hdr.protocol_id, xfer->hdr.seq, ret);
+
+		/* Wait also for an async delayed response if needed */
+		if (!ret && xfer->async_done) {
+			tmo = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT);
+			if (!wait_for_completion_timeout(xfer->async_done, tmo))
+				dev_err(dev,
+					"timed out in RAW delayed resp - HDR:%08X\n",
+					pack_scmi_header(&xfer->hdr));
+		}
+
+		/* Release waiter and xfer */
+		scmi_xfer_raw_put(raw->cinfo->handle, xfer);
+		scmi_xfer_raw_waiter_put(wrk, rw);
+	} while (1);
+}
+
+static int scmi_xfer_raw_enable(struct scmi_raw_mode_info *raw, bool enable)
+{
+	struct device *dev = raw->cinfo->handle->dev;
+
+	if (enable && !raw->enabled) {
+		dev_info(dev, "Enabling SCMI Raw access. Unbinding drivers.\n");
+		scmi_bus_raw_mode_enable();
+
+		/* Make sure all changes are visible before enabling Raw mode */
+		smp_store_mb(raw->enabled, true);
+	} else if (!enable && raw->enabled) {
+		//TODO Check about xfers registered as in-flight BUT never sent
+		int i;
+
+		/* Make sure all changes are visible before disabling Raw mode */
+		smp_store_mb(raw->enabled, false);
+
+		/*
+		 * Flush all message queues since nobody will read/dequeue these
+		 * anymore. Note that, instead, active_waiters queue will flush
+		 * on its own as soon as replies are received or time out.
+		 */
+		for (i = 0; i < SCMI_RAW_MAX_QUEUE; i++)
+			scmi_raw_buffer_queue_flush(raw, i);
+
+		dev_info(dev, "Disabling SCMI Raw access. Binding drivers.\n");
+		scmi_bus_raw_mode_disable();
+	}
+
+	return 0;
+}
+
+static int scmi_xfer_raw_get_init(struct scmi_raw_mode_info *raw, void *buf,
+				  size_t len, struct scmi_xfer **p)
+{
+	u32 msg_hdr;
+	size_t tx_size;
+	struct scmi_xfer *xfer;
+	int ret, retry = SCMI_XFER_RAW_MAX_RETRIES;
+	struct device *dev = raw->cinfo->handle->dev;
+
+	if (!buf || len < sizeof(u32))
+		return -EINVAL;
+
+	tx_size = len - sizeof(u32);
+	/* Ensure we have sane transfer sizes */
+	if (tx_size > raw->desc->max_msg_size)
+		return -ERANGE;
+
+	xfer = scmi_xfer_raw_get(raw->cinfo->handle);
+	if (IS_ERR(xfer)) {
+		dev_warn(dev, "RAW - Cannot get a free RAW xfer !\n");
+		return PTR_ERR(xfer);
+	}
+
+	/* Build xfer from provided SCMI bare message */
+	msg_hdr = le32_to_cpu(*((u32 *)buf));
+	unpack_scmi_header(msg_hdr, &xfer->hdr);
+	xfer->hdr.seq = (u16)MSG_XTRACT_TOKEN(msg_hdr);
+	xfer->hdr.poll_completion = false;
+	xfer->hdr.status = SCMI_SUCCESS;
+	xfer->tx.len = tx_size;
+	xfer->rx.len = raw->desc->max_msg_size;
+	memset(xfer->tx.buf, 0x00, raw->desc->max_msg_size);
+	if (tx_size)
+		memcpy(xfer->tx.buf, (u8 *)buf + sizeof(msg_hdr), tx_size);
+	*p = xfer;
+
+	/*
+	 * In flight registration can temporarily fail in case of Raw messages
+	 * if the user injects messages without using monotonically increasing
+	 * sequence numbers since the xfer (annd the token) is finally released
+	 * by a deferred worker in Raw mode. Just retry.
+	 */
+	do {
+		ret = scmi_xfer_raw_inflight_register(raw->cinfo->handle, xfer);
+		if (ret) {
+			dev_warn(dev,
+				 "RAW - Cannot register xfer %d as in-flight - HDR:0x%08X\n",
+				 xfer->hdr.seq, msg_hdr);
+
+			if (retry) {
+				dev_info(dev,
+					 "...retrying[%d] inflight registration\n",
+					 retry);
+				msleep(raw->desc->max_rx_timeout_ms /
+						SCMI_XFER_RAW_MAX_RETRIES);
+			} else {
+				scmi_xfer_raw_put(raw->cinfo->handle, xfer);
+			}
+		}
+	} while (ret && retry--);
+
+	return ret;
+}
+
+static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw,
+				  struct scmi_xfer *xfer, bool async)
+{
+	int ret;
+	struct scmi_xfer_raw_waiter *rw;
+	struct device *dev = raw->cinfo->handle->dev;
+	struct scmi_chan_info *cinfo = raw->cinfo;
+
+	rw = scmi_xfer_raw_waiter_get(raw->wrk, xfer, async);
+	if (!rw) {
+		dev_warn(dev, "RAW - Cannot get a free waiter !\n");
+		return -ENOMEM;
+	}
+
+	trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id,
+			      xfer->hdr.protocol_id, xfer->hdr.seq,
+			      xfer->hdr.poll_completion);
+
+	reinit_completion(&xfer->done);
+	/* Make sure xfer state update is visible before sending */
+	smp_store_mb(xfer->state, SCMI_XFER_SENT_OK);
+
+	ret = raw->desc->ops->send_message(cinfo, xfer);
+	if (ret) {
+		dev_err(dev, "Failed to send RAW message %d\n", ret);
+		scmi_xfer_raw_waiter_put(raw->wrk, rw);
+		return ret;
+	}
+
+	trace_scmi_msg_dump(xfer->hdr.protocol_id, xfer->hdr.id, "CMND",
+			    xfer->hdr.seq, xfer->hdr.status,
+			    xfer->tx.buf, xfer->tx.len);
+
+	scmi_xfer_raw_waiter_enqueue(raw->wrk, rw);
+
+	return ret;
+}
+
+static int scmi_raw_message_send(struct scmi_raw_mode_info *raw,
+				 void *buf, size_t len, bool async)
+{
+	int ret;
+	struct scmi_xfer *xfer;
+
+	if (!raw->enabled)
+		return -ENODEV;
+
+	ret = scmi_xfer_raw_get_init(raw, buf, len, &xfer);
+	if (ret)
+		return ret;
+
+	ret = scmi_do_xfer_raw_start(raw, xfer, async);
+	if (ret)
+		scmi_xfer_raw_put(raw->cinfo->handle, xfer);
+
+	return ret;
+}
+
+static struct scmi_raw_buffer *
+scmi_raw_message_dequeue(struct scmi_raw_mode_info *raw, unsigned int idx)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&raw->msg_q_lock[idx], flags);
+	while (list_empty(&raw->msg_q[idx])) {
+		spin_unlock_irqrestore(&raw->msg_q_lock[idx], flags);
+
+		if (wait_event_interruptible(raw->wqs[idx],
+					     !list_empty(&raw->msg_q[idx])))
+			return NULL;
+
+		spin_lock_irqsave(&raw->msg_q_lock[idx], flags);
+	}
+	spin_unlock_irqrestore(&raw->msg_q_lock[idx], flags);
+
+	return scmi_raw_buffer_dequeue(raw, idx);
+}
+
+static int scmi_raw_message_receive(struct scmi_raw_mode_info *raw,
+				    void *buf, size_t len, size_t *size,
+				    unsigned int idx)
+{
+	int ret = 0;
+	struct scmi_raw_buffer *rb;
+
+	if (!raw->enabled)
+		return -ENODEV;
+
+	rb = scmi_raw_message_dequeue(raw, idx);
+	if (!rb) {
+		dev_warn(raw->cinfo->handle->dev,
+			 "RAW - No message available!\n");
+		return -ENODEV;
+	}
+
+	if (rb->msg.len <= len) {
+		memcpy(buf, rb->msg.buf, rb->msg.len);
+		*size = rb->msg.len;
+	} else {
+		ret = -ENOSPC;
+	}
+
+	scmi_raw_buffer_put(raw, rb, idx);
+
+	return ret;
+}
+
+static int scmi_xfer_raw_collect(struct scmi_xfer *xfer,
+				 void *msg, size_t *msg_len)
+{
+	u32 *m;
+	size_t msg_size;
+
+	if (!xfer || !msg || !msg_len)
+		return -EINVAL;
+
+	/* Account for hdr ...*/
+	msg_size = xfer->rx.len + sizeof(u32);
+	/* ... and status if needed */
+	if (xfer->hdr.type != MSG_TYPE_NOTIFICATION)
+		msg_size += sizeof(u32);
+
+	if (msg_size > *msg_len)
+		return -ENOSPC;
+
+	m = msg;
+	*m = cpu_to_le32(pack_scmi_header(&xfer->hdr));
+	if (xfer->hdr.type != MSG_TYPE_NOTIFICATION)
+		*++m = cpu_to_le32(xfer->hdr.status);
+
+	memcpy(++m, xfer->rx.buf, xfer->rx.len);
+
+	*msg_len = msg_size;
+
+	return 0;
+}
+
+void scmi_raw_message_report(void *r, struct scmi_xfer *xfer, unsigned int idx)
+{
+	int ret;
+	struct scmi_raw_buffer *rb;
+	struct device *dev;
+	struct scmi_raw_mode_info *raw = r;
+
+	if (!raw || !raw->enabled)
+		return;
+
+	dev = raw->cinfo->handle->dev;
+	rb = scmi_raw_buffer_get(raw, idx);
+	if (!rb) {
+		dev_warn(dev, "RAW[%d] - Cannot get a free RAW buffer\n", idx);
+		return;
+	}
+
+	ret = scmi_xfer_raw_collect(xfer, rb->msg.buf, &rb->msg.len);
+	if (ret) {
+		dev_warn(dev, "RAW - Cannot collect xfer into buffer !\n");
+		scmi_raw_buffer_put(raw, rb, idx);
+		return;
+	}
+
+	scmi_raw_buffer_enqueue(raw, rb, idx);
+}
+
+static void scmi_xfer_raw_fill(struct scmi_raw_mode_info *raw,
+			       struct scmi_xfer *xfer, u32 msg_hdr)
+{
+	/* Unpack received HDR as it is */
+	unpack_scmi_header(msg_hdr, &xfer->hdr);
+	xfer->hdr.seq = MSG_XTRACT_TOKEN(msg_hdr);
+
+	memset(xfer->rx.buf, 0x00, xfer->rx.len);
+
+	raw->desc->ops->fetch_response(raw->cinfo, xfer);
+}
+
+void scmi_raw_error_report(void *r, u32 msg_hdr, void *priv)
+{
+	struct scmi_xfer xfer;
+	struct scmi_raw_buffer *rb;
+	struct scmi_raw_mode_info *raw = r;
+
+	if (!raw || !raw->enabled)
+		return;
+
+	rb = scmi_raw_buffer_get(raw, SCMI_RAW_ERRS_QUEUE);
+	if (!rb) {
+		dev_warn(raw->cinfo->handle->dev,
+			 "RAW[%d] - Cannot get a free RAW buffer\n",
+			 SCMI_RAW_ERRS_QUEUE);
+		return;
+	}
+
+	/* Use a raw buffer to provide rx space to the temp xfer */
+	xfer.rx.buf = rb->msg.buf;
+	/*
+	 * Allow max_msg_size...note that allocated rx.buf length is
+	 * max_msg_size + sizeof(u32).
+	 */
+	xfer.rx.len = raw->desc->max_msg_size;
+	if (priv)
+		/*
+		 * Any transport-provided priv must be passed back down
+		 * to transport
+		 */
+		smp_store_mb(xfer.priv, priv);
+
+	scmi_xfer_raw_fill(raw, &xfer, msg_hdr);
+	scmi_raw_message_report(raw, &xfer, SCMI_RAW_ERRS_QUEUE);
+
+	scmi_raw_buffer_put(raw, rb, SCMI_RAW_ERRS_QUEUE);
+}
+
+static inline ssize_t scmi_dbg_raw_mode_common_read(struct file *filp,
+						    char __user *buf,
+						    size_t count, loff_t *ppos,
+						    unsigned int idx)
+{
+	ssize_t cnt;
+	struct scmi_raw_data *rd = filp->private_data;
+
+	if (!rd->rx_size) {
+		int ret;
+
+		ret = scmi_raw_message_receive(rd->raw, rd->rx.buf, rd->rx.len,
+					       &rd->rx_size, idx);
+		if (ret) {
+			rd->rx_size = 0;
+			return ret;
+		}
+
+		/* Reset any previous filepos change, including writes */
+		*ppos = 0;
+	} else if (*ppos == rd->rx_size) {
+		/* Return EOF once all the message has been read-out */
+		rd->rx_size = 0;
+		return 0;
+	}
+
+	cnt = simple_read_from_buffer(buf, count, ppos,
+				      rd->rx.buf, rd->rx_size);
+
+	return cnt;
+}
+
+static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp,
+					      const char __user *buf,
+					      size_t count, loff_t *ppos,
+					      bool async)
+{
+	int ret;
+	struct scmi_raw_data *rd = filp->private_data;
+
+	if (count > rd->tx.len - rd->tx_size)
+		return -ENOSPC;
+
+	/* On first write attempt @count carries the total full message size. */
+	if (!rd->tx_size)
+		rd->tx_req = count;
+
+	/* Gather a full message before sending it with a RAW xfer */
+	if (rd->tx_size < rd->tx_req) {
+		size_t cnt;
+
+		cnt = simple_write_to_buffer(rd->tx.buf, rd->tx.len, ppos,
+					     buf, count);
+		rd->tx_size += cnt;
+		if (cnt < count)
+			return cnt;
+	}
+
+	ret = scmi_raw_message_send(rd->raw, rd->tx.buf, rd->tx_size, async);
+
+	/* Reset ppos for next message ... */
+	rd->tx_size = 0;
+
+	return ret ?: count;
+}
+
+static inline __poll_t
+scmi_test_dbg_raw_common_poll(struct file *filp, struct poll_table_struct *wait,
+			      unsigned int idx)
+{
+	unsigned long flags;
+	struct scmi_raw_data *rd = filp->private_data;
+
+	if (!rd->raw->enabled)
+		return 0;
+
+	poll_wait(filp, &rd->raw->wqs[idx], wait);
+
+	spin_lock_irqsave(&rd->raw->msg_q_lock[idx], flags);
+	if (!list_empty(&rd->raw->msg_q[idx])) {
+		spin_unlock_irqrestore(&rd->raw->msg_q_lock[idx], flags);
+		return POLLIN | POLLRDNORM;
+	}
+	spin_unlock_irqrestore(&rd->raw->msg_q_lock[idx], flags);
+
+	return 0;
+}
+
+static ssize_t scmi_dbg_raw_mode_message_read(struct file *filp,
+					      char __user *buf,
+					      size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
+					     SCMI_RAW_REPLY_QUEUE);
+}
+
+static ssize_t scmi_dbg_raw_mode_message_write(struct file *filp,
+					       const char __user *buf,
+					       size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, false);
+}
+
+static __poll_t scmi_dbg_raw_mode_message_poll(struct file *filp,
+					       struct poll_table_struct *wait)
+{
+	return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_REPLY_QUEUE);
+}
+
+static int scmi_dbg_raw_mode_open(struct inode *inode, struct file *filp)
+{
+	struct scmi_raw_mode_info *raw;
+	struct scmi_raw_data *rd;
+
+	if (!inode->i_private)
+		return -ENODEV;
+
+	raw = inode->i_private;
+	rd = kzalloc(sizeof(*rd), GFP_KERNEL);
+	if (!rd)
+		return -ENOMEM;
+
+	rd->rx.len = raw->desc->max_msg_size + sizeof(u32);
+	rd->rx.buf = kzalloc(rd->rx.len, GFP_KERNEL);
+	if (!rd->rx.buf) {
+		kfree(rd);
+		return -ENOMEM;
+	}
+
+	rd->tx.len = raw->desc->max_msg_size + sizeof(u32);
+	rd->tx.buf = kzalloc(rd->tx.len, GFP_KERNEL);
+	if (!rd->tx.buf) {
+		kfree(rd->rx.buf);
+		kfree(rd);
+		return -ENOMEM;
+	}
+
+	rd->raw = raw;
+	filp->private_data = rd;
+
+	return 0;
+}
+
+static int scmi_dbg_raw_mode_release(struct inode *inode, struct file *filp)
+{
+	struct scmi_raw_data *rd = filp->private_data;
+
+	kfree(rd->rx.buf);
+	kfree(rd->tx.buf);
+	kfree(rd);
+
+	return 0;
+}
+
+static ssize_t scmi_dbg_raw_mode_enable_write(struct file *filp,
+					      const char __user *buf,
+					      size_t count, loff_t *ppos)
+{
+	int ret;
+	bool enabled;
+	struct scmi_raw_data *rd = filp->private_data;
+
+	ret = kstrtobool_from_user(buf, count, &enabled);
+	if (ret)
+		return ret;
+
+	ret = scmi_xfer_raw_enable(rd->raw, enabled);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static const struct file_operations scmi_dbg_raw_mode_enable_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.write = scmi_dbg_raw_mode_enable_write,
+	.owner = THIS_MODULE,
+};
+
+static const struct file_operations scmi_dbg_raw_mode_message_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.read = scmi_dbg_raw_mode_message_read,
+	.write = scmi_dbg_raw_mode_message_write,
+	.poll = scmi_dbg_raw_mode_message_poll,
+	.owner = THIS_MODULE,
+};
+
+static ssize_t scmi_dbg_raw_mode_message_async_write(struct file *filp,
+						     const char __user *buf,
+						     size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, true);
+}
+
+static const struct file_operations scmi_dbg_raw_mode_message_async_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.read = scmi_dbg_raw_mode_message_read,
+	.write = scmi_dbg_raw_mode_message_async_write,
+	.poll = scmi_dbg_raw_mode_message_poll,
+	.owner = THIS_MODULE,
+};
+
+static ssize_t scmi_test_dbg_raw_mode_notif_read(struct file *filp,
+						 char __user *buf,
+						 size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
+					     SCMI_RAW_NOTIF_QUEUE);
+}
+
+static __poll_t scmi_test_dbg_raw_mode_notif_poll(struct file *filp,
+						  struct poll_table_struct *wait)
+{
+	return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_NOTIF_QUEUE);
+}
+
+static const struct file_operations scmi_dbg_raw_mode_notification_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.read = scmi_test_dbg_raw_mode_notif_read,
+	.poll = scmi_test_dbg_raw_mode_notif_poll,
+	.owner = THIS_MODULE,
+};
+
+static ssize_t scmi_test_dbg_raw_mode_errors_read(struct file *filp,
+						  char __user *buf,
+						  size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
+					     SCMI_RAW_ERRS_QUEUE);
+}
+
+static __poll_t scmi_test_dbg_raw_mode_errors_poll(struct file *filp,
+						   struct poll_table_struct *wait)
+{
+	return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_ERRS_QUEUE);
+}
+
+static const struct file_operations scmi_dbg_raw_mode_errors_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.read = scmi_test_dbg_raw_mode_errors_read,
+	.poll = scmi_test_dbg_raw_mode_errors_poll,
+	.owner = THIS_MODULE,
+};
+
+static int scmi_xfer_raw_free_bufs_init(struct scmi_raw_mode_info *raw, int idx)
+{
+	int i;
+	struct scmi_raw_buffer *rb;
+	struct device *dev = raw->cinfo->handle->dev;
+
+	rb = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rb), GFP_KERNEL);
+	if (!rb)
+		return -ENOMEM;
+
+	spin_lock_init(&raw->free_bufs_lock[idx]);
+	INIT_LIST_HEAD(&raw->free_raw_bufs[idx]);
+	for (i = 0; i < raw->tx_max_msg; i++, rb++) {
+		rb->max_len = raw->desc->max_msg_size + sizeof(u32);
+		rb->msg.buf = devm_kzalloc(dev, rb->max_len, GFP_KERNEL);
+		if (!rb->msg.buf)
+			return -ENOMEM;
+		scmi_raw_buffer_put(raw, rb, idx);
+	}
+
+	spin_lock_init(&raw->msg_q_lock[idx]);
+	INIT_LIST_HEAD(&raw->msg_q[idx]);
+	init_waitqueue_head(&raw->wqs[idx]);
+
+	return 0;
+}
+
+static int scmi_xfer_raw_worker_init(struct scmi_raw_mode_info *raw)
+{
+	int i;
+	struct scmi_xfer_raw_waiter *rw;
+	struct device *dev = raw->cinfo->handle->dev;
+
+	rw = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rw), GFP_KERNEL);
+	if (!rw)
+		return -ENOMEM;
+
+	raw->wrk = devm_kzalloc(dev, sizeof(*raw->wrk), GFP_KERNEL);
+	if (!raw->wrk)
+		return -ENOMEM;
+
+	raw->wrk->wait_wq = alloc_workqueue("scmi-raw-wait-wq-%d",
+					    WQ_UNBOUND | WQ_FREEZABLE |
+					    WQ_HIGHPRI, WQ_SYSFS, 0);
+	if (!raw->wrk->wait_wq)
+		return -ENOMEM;
+
+	raw->wrk->max_tmo = msecs_to_jiffies(raw->desc->max_rx_timeout_ms);
+
+	raw->wrk->raw = raw;
+	mutex_init(&raw->wrk->free_mtx);
+	INIT_LIST_HEAD(&raw->wrk->active_waiters);
+	mutex_init(&raw->wrk->active_mtx);
+	INIT_LIST_HEAD(&raw->wrk->free_waiters);
+	for (i = 0; i < raw->tx_max_msg; i++, rw++) {
+		init_completion(&rw->async_response);
+		scmi_xfer_raw_waiter_put(raw->wrk, rw);
+	}
+	INIT_WORK(&raw->wrk->waiters_work, scmi_xfer_raw_worker);
+	init_waitqueue_head(&raw->wrk->waiters_wq);
+
+	/* kick waiter work */
+	queue_work(raw->wrk->wait_wq, &raw->wrk->waiters_work);
+
+	return 0;
+}
+
+static int scmi_raw_mode_setup(struct scmi_raw_mode_info *raw)
+{
+	int ret;
+	void *gid;
+	struct device *dev = raw->cinfo->handle->dev;
+
+	gid = devres_open_group(dev, NULL, GFP_KERNEL);
+	if (!gid)
+		return -ENOMEM;
+
+	ret = scmi_xfer_raw_free_bufs_init(raw, SCMI_RAW_REPLY_QUEUE);
+	if (ret)
+		goto err;
+
+	ret = scmi_xfer_raw_free_bufs_init(raw, SCMI_RAW_NOTIF_QUEUE);
+	if (ret)
+		goto err;
+
+	ret = scmi_xfer_raw_free_bufs_init(raw, SCMI_RAW_ERRS_QUEUE);
+	if (ret)
+		goto err;
+
+	ret = scmi_xfer_raw_worker_init(raw);
+	if (ret)
+		goto err;
+
+	devres_close_group(dev, gid);
+	raw->gid = gid;
+
+	return 0;
+
+err:
+	devres_release_group(dev, gid);
+	return ret;
+}
+
+void *scmi_raw_mode_init(struct scmi_chan_info *cinfo,
+			 const struct scmi_desc *desc, int tx_max_msg)
+{
+	int ret;
+	struct scmi_raw_mode_info *raw;
+	struct device *dev;
+
+	if (!cinfo || !desc)
+		return ERR_PTR(-EINVAL);
+
+	dev = cinfo->handle->dev;
+	raw = devm_kzalloc(dev, sizeof(*raw), GFP_KERNEL);
+	if (!raw)
+		return ERR_PTR(-ENOMEM);
+
+	raw->cinfo = cinfo;
+	raw->desc = desc;
+	raw->tx_max_msg = tx_max_msg;
+
+	ret = scmi_raw_mode_setup(raw);
+	if (ret) {
+		devm_kfree(dev, raw);
+		return ERR_PTR(ret);
+	}
+
+	raw->dentry = debugfs_create_dir("scmi_raw", NULL);
+	if (IS_ERR(raw->dentry)) {
+		ret = PTR_ERR(raw->dentry);
+		devres_release_group(dev, raw->gid);
+		devm_kfree(dev, raw);
+		return ERR_PTR(ret);
+	}
+
+	debugfs_create_file("enable", 0200, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_enable_fops);
+
+	debugfs_create_u32("transport_rx_timeout_ms", 0400, raw->dentry,
+			   (u32 *)&raw->desc->max_rx_timeout_ms);
+
+	debugfs_create_u32("transport_max_msg_size", 0400, raw->dentry,
+			   (u32 *)&raw->desc->max_msg_size);
+
+	debugfs_create_u32("transport_tx_max_msg", 0400, raw->dentry,
+			   (u32 *)&raw->tx_max_msg);
+
+	debugfs_create_file("message", 0600, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_message_fops);
+
+	debugfs_create_file("message_async", 0600, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_message_async_fops);
+
+	debugfs_create_file("notification", 0400, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_notification_fops);
+
+	debugfs_create_file("errors", 0400, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_errors_fops);
+
+	return raw;
+}
+
+void scmi_raw_mode_cleanup(void *r)
+{
+	struct scmi_raw_mode_info *raw = r;
+
+	if (!raw)
+		return;
+
+	debugfs_remove_recursive(raw->dentry);
+	scmi_xfer_raw_enable(raw, false);
+
+	raw->wrk->wait_wq_shutdown = true;
+	wake_up_interruptible(&raw->wrk->waiters_wq);
+	cancel_work_sync(&raw->wrk->waiters_work);
+	destroy_workqueue(raw->wrk->wait_wq);
+}
diff --git a/drivers/firmware/arm_scmi/raw_mode.h b/drivers/firmware/arm_scmi/raw_mode.h
new file mode 100644
index 000000000000..9988fd31ec91
--- /dev/null
+++ b/drivers/firmware/arm_scmi/raw_mode.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * System Control and Management Interface (SCMI) Message Protocol
+ * Raw mode support header.
+ *
+ * Copyright (C) 2022 ARM Ltd.
+ */
+#ifndef _SCMI_RAW_MODE_H
+#define _SCMI_RAW_MODE_H
+
+#include "common.h"
+
+enum {
+	SCMI_RAW_REPLY_QUEUE,
+	SCMI_RAW_NOTIF_QUEUE,
+	SCMI_RAW_ERRS_QUEUE,
+	SCMI_RAW_MAX_QUEUE
+};
+
+void *scmi_raw_mode_init(struct scmi_chan_info *cinfo,
+			 const struct scmi_desc *desc, int tx_max_msg);
+void scmi_raw_mode_cleanup(void *raw);
+
+void scmi_raw_message_report(void *raw, struct scmi_xfer *xfer,
+			     unsigned int idx);
+void scmi_raw_error_report(void *raw, u32 msg_hdr, void *priv);
+
+#endif /* _SCMI_RAW_MODE_H */
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
@ 2022-08-16  7:24   ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Add SCMI Raw mode support which exposes a userspace interface rooted under
/sys/kernel/debug/scmi_raw.

Raw mode can be enabled/disabled at runtime via ./scmi_raw/enable.
Once enabled, all the regular SCMI drivers activity is inhibited and a
userspace application can then inject and read back bare SCMI messages
writing and reading to/from ./scmi_raw/message* entries.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/Kconfig    |   13 +
 drivers/firmware/arm_scmi/Makefile   |    1 +
 drivers/firmware/arm_scmi/raw_mode.c | 1046 ++++++++++++++++++++++++++
 drivers/firmware/arm_scmi/raw_mode.h |   28 +
 4 files changed, 1088 insertions(+)
 create mode 100644 drivers/firmware/arm_scmi/raw_mode.c
 create mode 100644 drivers/firmware/arm_scmi/raw_mode.h

diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig
index a14f65444b35..ab726a92ac2f 100644
--- a/drivers/firmware/arm_scmi/Kconfig
+++ b/drivers/firmware/arm_scmi/Kconfig
@@ -23,6 +23,19 @@ config ARM_SCMI_PROTOCOL
 
 if ARM_SCMI_PROTOCOL
 
+config ARM_SCMI_RAW_MODE_SUPPORT
+	bool "Enable support for SCMI Raw transmission mode"
+	help
+	  Enable support for SCMI Raw transmission mode.
+
+	  If enabled allows the direct injection and snooping of SCMI bare
+	  messages through a dedicated debugfs interface.
+	  It is meant to be used by SCMI compliance/testing suites.
+
+	  When enabled regular SCMI drivers interactions are inhibited in
+	  order to avoid unexpected interactions with the SCMI Raw message
+	  flow. If unsure say N.
+
 config ARM_SCMI_HAVE_TRANSPORT
 	bool
 	help
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 9ea86f8cc8f7..7c1aca60c8ce 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 scmi-bus-y = bus.o
 scmi-driver-y = driver.o notify.o
+scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o
 scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o
 scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += mailbox.o
 scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
diff --git a/drivers/firmware/arm_scmi/raw_mode.c b/drivers/firmware/arm_scmi/raw_mode.c
new file mode 100644
index 000000000000..c14cdd12b140
--- /dev/null
+++ b/drivers/firmware/arm_scmi/raw_mode.c
@@ -0,0 +1,1046 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * System Control and Management Interface (SCMI) Raw mode support
+ *
+ * Copyright (C) 2022 ARM Ltd.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/slab.h>
+
+#include "common.h"
+
+#include "raw_mode.h"
+
+#include <trace/events/scmi.h>
+
+#define SCMI_XFER_RAW_MAX_RETRIES	10
+
+struct scmi_xfer_raw_work {
+	unsigned long max_tmo;
+	struct scmi_raw_mode_info *raw;
+	struct list_head free_waiters;
+	/* Protect free_waiters list */
+	struct mutex free_mtx;
+	struct list_head active_waiters;
+	/* Protect active_waiters list */
+	struct mutex active_mtx;
+	wait_queue_head_t waiters_wq;
+	struct work_struct waiters_work;
+	bool wait_wq_shutdown;
+	struct workqueue_struct	*wait_wq;
+};
+
+struct scmi_raw_mode_info {
+	bool enabled;
+	struct scmi_chan_info *cinfo;
+	const struct scmi_desc *desc;
+	int tx_max_msg;
+	struct list_head free_raw_bufs[SCMI_RAW_MAX_QUEUE];
+	/* Protect free_raw_bufs[] lists */
+	spinlock_t free_bufs_lock[SCMI_RAW_MAX_QUEUE];
+	struct list_head msg_q[SCMI_RAW_MAX_QUEUE];
+	/* Protect msg_q[] lists */
+	spinlock_t msg_q_lock[SCMI_RAW_MAX_QUEUE];
+	wait_queue_head_t wqs[SCMI_RAW_MAX_QUEUE];
+	struct scmi_xfer_raw_work *wrk;
+	struct dentry *dentry;
+	void *gid;
+};
+
+struct scmi_xfer_raw_waiter {
+	unsigned long start_jiffies;
+	struct scmi_xfer *xfer;
+	struct completion async_response;
+	struct list_head node;
+};
+
+struct scmi_raw_buffer {
+	size_t max_len;
+	struct scmi_msg msg;
+	struct list_head node;
+};
+
+struct scmi_raw_data {
+	struct scmi_raw_mode_info *raw;
+	struct scmi_msg tx;
+	size_t tx_size;
+	size_t tx_req;
+	struct scmi_msg rx;
+	size_t rx_size;
+};
+
+static inline
+struct scmi_raw_buffer *scmi_raw_buffer_get(struct scmi_raw_mode_info *raw,
+					    unsigned int idx)
+{
+	unsigned long flags;
+	struct scmi_raw_buffer *rb = NULL;
+	struct list_head *head = &raw->free_raw_bufs[idx];
+
+	spin_lock_irqsave(&raw->free_bufs_lock[idx], flags);
+	if (!list_empty(head)) {
+		rb = list_first_entry(head, struct scmi_raw_buffer, node);
+		list_del_init(&rb->node);
+	}
+	spin_unlock_irqrestore(&raw->free_bufs_lock[idx], flags);
+
+	return rb;
+}
+
+static inline void scmi_raw_buffer_put(struct scmi_raw_mode_info *raw,
+				       struct scmi_raw_buffer *rb,
+				       unsigned int idx)
+{
+	unsigned long flags;
+
+	rb->msg.len = rb->max_len;
+
+	spin_lock_irqsave(&raw->free_bufs_lock[idx], flags);
+	list_add_tail(&rb->node, &raw->free_raw_bufs[idx]);
+	spin_unlock_irqrestore(&raw->free_bufs_lock[idx], flags);
+}
+
+static inline void scmi_raw_buffer_enqueue(struct scmi_raw_mode_info *raw,
+					   struct scmi_raw_buffer *rb,
+					   unsigned int idx)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&raw->msg_q_lock[idx], flags);
+	list_add_tail(&rb->node, &raw->msg_q[idx]);
+	spin_unlock_irqrestore(&raw->msg_q_lock[idx], flags);
+
+	wake_up_interruptible(&raw->wqs[idx]);
+}
+
+static inline struct scmi_raw_buffer*
+scmi_raw_buffer_dequeue(struct scmi_raw_mode_info *raw, unsigned int idx)
+{
+	unsigned long flags;
+	struct scmi_raw_buffer *rb = NULL;
+
+	spin_lock_irqsave(&raw->msg_q_lock[idx], flags);
+	if (!list_empty(&raw->msg_q[idx])) {
+		rb = list_first_entry(&raw->msg_q[idx],
+				      struct scmi_raw_buffer, node);
+		list_del_init(&rb->node);
+	}
+	spin_unlock_irqrestore(&raw->msg_q_lock[idx], flags);
+
+	return rb;
+}
+
+static void scmi_raw_buffer_queue_flush(struct scmi_raw_mode_info *raw,
+					unsigned int idx)
+{
+	struct scmi_raw_buffer *rb;
+
+	do {
+		rb = scmi_raw_buffer_dequeue(raw, idx);
+		if (rb)
+			scmi_raw_buffer_put(raw, rb, idx);
+	} while (rb);
+}
+
+static inline struct scmi_xfer_raw_waiter *
+scmi_xfer_raw_waiter_get(struct scmi_xfer_raw_work *wrk, struct scmi_xfer *xfer,
+			 bool async)
+{
+	struct scmi_xfer_raw_waiter *rw = NULL;
+
+	mutex_lock(&wrk->free_mtx);
+	if (!list_empty(&wrk->free_waiters)) {
+		rw = list_first_entry(&wrk->free_waiters,
+				      struct scmi_xfer_raw_waiter, node);
+		list_del_init(&rw->node);
+
+		if (async) {
+			reinit_completion(&rw->async_response);
+			xfer->async_done = &rw->async_response;
+		}
+
+		rw->xfer = xfer;
+	}
+	//TODO this critS can be shortened...
+	mutex_unlock(&wrk->free_mtx);
+
+	return rw;
+}
+
+static inline
+void scmi_xfer_raw_waiter_put(struct scmi_xfer_raw_work *wrk,
+			      struct scmi_xfer_raw_waiter *rw)
+{
+	if (rw->xfer) {
+		rw->xfer->async_done = NULL;
+		rw->xfer = NULL;
+	}
+
+	mutex_lock(&wrk->free_mtx);
+	list_add_tail(&rw->node, &wrk->free_waiters);
+	mutex_unlock(&wrk->free_mtx);
+}
+
+static inline
+void scmi_xfer_raw_waiter_enqueue(struct scmi_xfer_raw_work *wrk,
+				  struct scmi_xfer_raw_waiter *rw)
+{
+	rw->start_jiffies = jiffies;
+
+	trace_scmi_xfer_response_wait(rw->xfer->transfer_id, rw->xfer->hdr.id,
+				      rw->xfer->hdr.protocol_id,
+				      rw->xfer->hdr.seq,
+				      wrk->raw->desc->max_rx_timeout_ms,
+				      rw->xfer->hdr.poll_completion);
+
+	mutex_lock(&wrk->active_mtx);
+	list_add_tail(&rw->node, &wrk->active_waiters);
+	mutex_unlock(&wrk->active_mtx);
+
+	wake_up_interruptible(&wrk->waiters_wq);
+}
+
+static struct scmi_xfer_raw_waiter*
+scmi_xfer_raw_waiter_dequeue(struct scmi_xfer_raw_work *wrk)
+{
+	struct scmi_xfer_raw_waiter *rw;
+
+	mutex_lock(&wrk->active_mtx);
+	while (list_empty(&wrk->active_waiters)) {
+		int ret;
+
+		mutex_unlock(&wrk->active_mtx);
+
+		ret = wait_event_interruptible(wrk->waiters_wq,
+					       !list_empty(&wrk->active_waiters) ||
+					       wrk->wait_wq_shutdown);
+		if (ret || wrk->wait_wq_shutdown)
+			return NULL;
+
+		mutex_lock(&wrk->active_mtx);
+	}
+
+	rw = list_first_entry(&wrk->active_waiters,
+			      struct scmi_xfer_raw_waiter, node);
+	list_del_init(&rw->node);
+	mutex_unlock(&wrk->active_mtx);
+
+	return rw;
+}
+
+static void scmi_xfer_raw_worker(struct work_struct *work)
+{
+	struct scmi_xfer_raw_work *wrk;
+	struct scmi_raw_mode_info *raw;
+	struct scmi_chan_info *cinfo;
+	struct device *dev;
+
+	wrk = container_of(work, struct scmi_xfer_raw_work, waiters_work);
+
+	raw = wrk->raw;
+	cinfo = raw->cinfo;
+	dev = raw->cinfo->handle->dev;
+
+	do {
+		int ret = 0;
+		unsigned long aging, tmo;
+		struct scmi_xfer *xfer;
+		struct scmi_xfer_raw_waiter *rw;
+
+		/*
+		 * Waiters are queued by wait-deadline at the end, so some of
+		 * them could have been already expired when processed, BUT we
+		 * have to check the completion status anyway just in case a
+		 * virtually expired (aged) transaction was indeed completed
+		 * fine and we'll have to wait for the asynchronous part (if
+		 * any).
+		 */
+		rw = scmi_xfer_raw_waiter_dequeue(wrk);
+		if (!rw)
+			return;
+
+		xfer = rw->xfer;
+
+		aging = jiffies - rw->start_jiffies;
+		tmo = wrk->max_tmo > aging ? wrk->max_tmo - aging : 0;
+
+		if ((tmo && !wait_for_completion_timeout(&xfer->done, tmo)) ||
+		    (!tmo && !try_wait_for_completion(&xfer->done))) {
+			dev_err(dev, "timed out in RAW response - HDR:%08X\n",
+				pack_scmi_header(&xfer->hdr));
+			ret = -ETIMEDOUT;
+		}
+
+		/* Avoid unneeded async waits */
+		if (!ret && xfer->hdr.status)
+			ret = scmi_to_linux_errno(xfer->hdr.status);
+
+		if (raw->desc->ops->mark_txdone)
+			raw->desc->ops->mark_txdone(cinfo, ret, xfer);
+
+		trace_scmi_xfer_end(xfer->transfer_id, xfer->hdr.id,
+				    xfer->hdr.protocol_id, xfer->hdr.seq, ret);
+
+		/* Wait also for an async delayed response if needed */
+		if (!ret && xfer->async_done) {
+			tmo = msecs_to_jiffies(SCMI_MAX_RESPONSE_TIMEOUT);
+			if (!wait_for_completion_timeout(xfer->async_done, tmo))
+				dev_err(dev,
+					"timed out in RAW delayed resp - HDR:%08X\n",
+					pack_scmi_header(&xfer->hdr));
+		}
+
+		/* Release waiter and xfer */
+		scmi_xfer_raw_put(raw->cinfo->handle, xfer);
+		scmi_xfer_raw_waiter_put(wrk, rw);
+	} while (1);
+}
+
+static int scmi_xfer_raw_enable(struct scmi_raw_mode_info *raw, bool enable)
+{
+	struct device *dev = raw->cinfo->handle->dev;
+
+	if (enable && !raw->enabled) {
+		dev_info(dev, "Enabling SCMI Raw access. Unbinding drivers.\n");
+		scmi_bus_raw_mode_enable();
+
+		/* Make sure all changes are visible before enabling Raw mode */
+		smp_store_mb(raw->enabled, true);
+	} else if (!enable && raw->enabled) {
+		//TODO Check about xfers registered as in-flight BUT never sent
+		int i;
+
+		/* Make sure all changes are visible before disabling Raw mode */
+		smp_store_mb(raw->enabled, false);
+
+		/*
+		 * Flush all message queues since nobody will read/dequeue these
+		 * anymore. Note that, instead, active_waiters queue will flush
+		 * on its own as soon as replies are received or time out.
+		 */
+		for (i = 0; i < SCMI_RAW_MAX_QUEUE; i++)
+			scmi_raw_buffer_queue_flush(raw, i);
+
+		dev_info(dev, "Disabling SCMI Raw access. Binding drivers.\n");
+		scmi_bus_raw_mode_disable();
+	}
+
+	return 0;
+}
+
+static int scmi_xfer_raw_get_init(struct scmi_raw_mode_info *raw, void *buf,
+				  size_t len, struct scmi_xfer **p)
+{
+	u32 msg_hdr;
+	size_t tx_size;
+	struct scmi_xfer *xfer;
+	int ret, retry = SCMI_XFER_RAW_MAX_RETRIES;
+	struct device *dev = raw->cinfo->handle->dev;
+
+	if (!buf || len < sizeof(u32))
+		return -EINVAL;
+
+	tx_size = len - sizeof(u32);
+	/* Ensure we have sane transfer sizes */
+	if (tx_size > raw->desc->max_msg_size)
+		return -ERANGE;
+
+	xfer = scmi_xfer_raw_get(raw->cinfo->handle);
+	if (IS_ERR(xfer)) {
+		dev_warn(dev, "RAW - Cannot get a free RAW xfer !\n");
+		return PTR_ERR(xfer);
+	}
+
+	/* Build xfer from provided SCMI bare message */
+	msg_hdr = le32_to_cpu(*((u32 *)buf));
+	unpack_scmi_header(msg_hdr, &xfer->hdr);
+	xfer->hdr.seq = (u16)MSG_XTRACT_TOKEN(msg_hdr);
+	xfer->hdr.poll_completion = false;
+	xfer->hdr.status = SCMI_SUCCESS;
+	xfer->tx.len = tx_size;
+	xfer->rx.len = raw->desc->max_msg_size;
+	memset(xfer->tx.buf, 0x00, raw->desc->max_msg_size);
+	if (tx_size)
+		memcpy(xfer->tx.buf, (u8 *)buf + sizeof(msg_hdr), tx_size);
+	*p = xfer;
+
+	/*
+	 * In flight registration can temporarily fail in case of Raw messages
+	 * if the user injects messages without using monotonically increasing
+	 * sequence numbers since the xfer (annd the token) is finally released
+	 * by a deferred worker in Raw mode. Just retry.
+	 */
+	do {
+		ret = scmi_xfer_raw_inflight_register(raw->cinfo->handle, xfer);
+		if (ret) {
+			dev_warn(dev,
+				 "RAW - Cannot register xfer %d as in-flight - HDR:0x%08X\n",
+				 xfer->hdr.seq, msg_hdr);
+
+			if (retry) {
+				dev_info(dev,
+					 "...retrying[%d] inflight registration\n",
+					 retry);
+				msleep(raw->desc->max_rx_timeout_ms /
+						SCMI_XFER_RAW_MAX_RETRIES);
+			} else {
+				scmi_xfer_raw_put(raw->cinfo->handle, xfer);
+			}
+		}
+	} while (ret && retry--);
+
+	return ret;
+}
+
+static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw,
+				  struct scmi_xfer *xfer, bool async)
+{
+	int ret;
+	struct scmi_xfer_raw_waiter *rw;
+	struct device *dev = raw->cinfo->handle->dev;
+	struct scmi_chan_info *cinfo = raw->cinfo;
+
+	rw = scmi_xfer_raw_waiter_get(raw->wrk, xfer, async);
+	if (!rw) {
+		dev_warn(dev, "RAW - Cannot get a free waiter !\n");
+		return -ENOMEM;
+	}
+
+	trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id,
+			      xfer->hdr.protocol_id, xfer->hdr.seq,
+			      xfer->hdr.poll_completion);
+
+	reinit_completion(&xfer->done);
+	/* Make sure xfer state update is visible before sending */
+	smp_store_mb(xfer->state, SCMI_XFER_SENT_OK);
+
+	ret = raw->desc->ops->send_message(cinfo, xfer);
+	if (ret) {
+		dev_err(dev, "Failed to send RAW message %d\n", ret);
+		scmi_xfer_raw_waiter_put(raw->wrk, rw);
+		return ret;
+	}
+
+	trace_scmi_msg_dump(xfer->hdr.protocol_id, xfer->hdr.id, "CMND",
+			    xfer->hdr.seq, xfer->hdr.status,
+			    xfer->tx.buf, xfer->tx.len);
+
+	scmi_xfer_raw_waiter_enqueue(raw->wrk, rw);
+
+	return ret;
+}
+
+static int scmi_raw_message_send(struct scmi_raw_mode_info *raw,
+				 void *buf, size_t len, bool async)
+{
+	int ret;
+	struct scmi_xfer *xfer;
+
+	if (!raw->enabled)
+		return -ENODEV;
+
+	ret = scmi_xfer_raw_get_init(raw, buf, len, &xfer);
+	if (ret)
+		return ret;
+
+	ret = scmi_do_xfer_raw_start(raw, xfer, async);
+	if (ret)
+		scmi_xfer_raw_put(raw->cinfo->handle, xfer);
+
+	return ret;
+}
+
+static struct scmi_raw_buffer *
+scmi_raw_message_dequeue(struct scmi_raw_mode_info *raw, unsigned int idx)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&raw->msg_q_lock[idx], flags);
+	while (list_empty(&raw->msg_q[idx])) {
+		spin_unlock_irqrestore(&raw->msg_q_lock[idx], flags);
+
+		if (wait_event_interruptible(raw->wqs[idx],
+					     !list_empty(&raw->msg_q[idx])))
+			return NULL;
+
+		spin_lock_irqsave(&raw->msg_q_lock[idx], flags);
+	}
+	spin_unlock_irqrestore(&raw->msg_q_lock[idx], flags);
+
+	return scmi_raw_buffer_dequeue(raw, idx);
+}
+
+static int scmi_raw_message_receive(struct scmi_raw_mode_info *raw,
+				    void *buf, size_t len, size_t *size,
+				    unsigned int idx)
+{
+	int ret = 0;
+	struct scmi_raw_buffer *rb;
+
+	if (!raw->enabled)
+		return -ENODEV;
+
+	rb = scmi_raw_message_dequeue(raw, idx);
+	if (!rb) {
+		dev_warn(raw->cinfo->handle->dev,
+			 "RAW - No message available!\n");
+		return -ENODEV;
+	}
+
+	if (rb->msg.len <= len) {
+		memcpy(buf, rb->msg.buf, rb->msg.len);
+		*size = rb->msg.len;
+	} else {
+		ret = -ENOSPC;
+	}
+
+	scmi_raw_buffer_put(raw, rb, idx);
+
+	return ret;
+}
+
+static int scmi_xfer_raw_collect(struct scmi_xfer *xfer,
+				 void *msg, size_t *msg_len)
+{
+	u32 *m;
+	size_t msg_size;
+
+	if (!xfer || !msg || !msg_len)
+		return -EINVAL;
+
+	/* Account for hdr ...*/
+	msg_size = xfer->rx.len + sizeof(u32);
+	/* ... and status if needed */
+	if (xfer->hdr.type != MSG_TYPE_NOTIFICATION)
+		msg_size += sizeof(u32);
+
+	if (msg_size > *msg_len)
+		return -ENOSPC;
+
+	m = msg;
+	*m = cpu_to_le32(pack_scmi_header(&xfer->hdr));
+	if (xfer->hdr.type != MSG_TYPE_NOTIFICATION)
+		*++m = cpu_to_le32(xfer->hdr.status);
+
+	memcpy(++m, xfer->rx.buf, xfer->rx.len);
+
+	*msg_len = msg_size;
+
+	return 0;
+}
+
+void scmi_raw_message_report(void *r, struct scmi_xfer *xfer, unsigned int idx)
+{
+	int ret;
+	struct scmi_raw_buffer *rb;
+	struct device *dev;
+	struct scmi_raw_mode_info *raw = r;
+
+	if (!raw || !raw->enabled)
+		return;
+
+	dev = raw->cinfo->handle->dev;
+	rb = scmi_raw_buffer_get(raw, idx);
+	if (!rb) {
+		dev_warn(dev, "RAW[%d] - Cannot get a free RAW buffer\n", idx);
+		return;
+	}
+
+	ret = scmi_xfer_raw_collect(xfer, rb->msg.buf, &rb->msg.len);
+	if (ret) {
+		dev_warn(dev, "RAW - Cannot collect xfer into buffer !\n");
+		scmi_raw_buffer_put(raw, rb, idx);
+		return;
+	}
+
+	scmi_raw_buffer_enqueue(raw, rb, idx);
+}
+
+static void scmi_xfer_raw_fill(struct scmi_raw_mode_info *raw,
+			       struct scmi_xfer *xfer, u32 msg_hdr)
+{
+	/* Unpack received HDR as it is */
+	unpack_scmi_header(msg_hdr, &xfer->hdr);
+	xfer->hdr.seq = MSG_XTRACT_TOKEN(msg_hdr);
+
+	memset(xfer->rx.buf, 0x00, xfer->rx.len);
+
+	raw->desc->ops->fetch_response(raw->cinfo, xfer);
+}
+
+void scmi_raw_error_report(void *r, u32 msg_hdr, void *priv)
+{
+	struct scmi_xfer xfer;
+	struct scmi_raw_buffer *rb;
+	struct scmi_raw_mode_info *raw = r;
+
+	if (!raw || !raw->enabled)
+		return;
+
+	rb = scmi_raw_buffer_get(raw, SCMI_RAW_ERRS_QUEUE);
+	if (!rb) {
+		dev_warn(raw->cinfo->handle->dev,
+			 "RAW[%d] - Cannot get a free RAW buffer\n",
+			 SCMI_RAW_ERRS_QUEUE);
+		return;
+	}
+
+	/* Use a raw buffer to provide rx space to the temp xfer */
+	xfer.rx.buf = rb->msg.buf;
+	/*
+	 * Allow max_msg_size...note that allocated rx.buf length is
+	 * max_msg_size + sizeof(u32).
+	 */
+	xfer.rx.len = raw->desc->max_msg_size;
+	if (priv)
+		/*
+		 * Any transport-provided priv must be passed back down
+		 * to transport
+		 */
+		smp_store_mb(xfer.priv, priv);
+
+	scmi_xfer_raw_fill(raw, &xfer, msg_hdr);
+	scmi_raw_message_report(raw, &xfer, SCMI_RAW_ERRS_QUEUE);
+
+	scmi_raw_buffer_put(raw, rb, SCMI_RAW_ERRS_QUEUE);
+}
+
+static inline ssize_t scmi_dbg_raw_mode_common_read(struct file *filp,
+						    char __user *buf,
+						    size_t count, loff_t *ppos,
+						    unsigned int idx)
+{
+	ssize_t cnt;
+	struct scmi_raw_data *rd = filp->private_data;
+
+	if (!rd->rx_size) {
+		int ret;
+
+		ret = scmi_raw_message_receive(rd->raw, rd->rx.buf, rd->rx.len,
+					       &rd->rx_size, idx);
+		if (ret) {
+			rd->rx_size = 0;
+			return ret;
+		}
+
+		/* Reset any previous filepos change, including writes */
+		*ppos = 0;
+	} else if (*ppos == rd->rx_size) {
+		/* Return EOF once all the message has been read-out */
+		rd->rx_size = 0;
+		return 0;
+	}
+
+	cnt = simple_read_from_buffer(buf, count, ppos,
+				      rd->rx.buf, rd->rx_size);
+
+	return cnt;
+}
+
+static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp,
+					      const char __user *buf,
+					      size_t count, loff_t *ppos,
+					      bool async)
+{
+	int ret;
+	struct scmi_raw_data *rd = filp->private_data;
+
+	if (count > rd->tx.len - rd->tx_size)
+		return -ENOSPC;
+
+	/* On first write attempt @count carries the total full message size. */
+	if (!rd->tx_size)
+		rd->tx_req = count;
+
+	/* Gather a full message before sending it with a RAW xfer */
+	if (rd->tx_size < rd->tx_req) {
+		size_t cnt;
+
+		cnt = simple_write_to_buffer(rd->tx.buf, rd->tx.len, ppos,
+					     buf, count);
+		rd->tx_size += cnt;
+		if (cnt < count)
+			return cnt;
+	}
+
+	ret = scmi_raw_message_send(rd->raw, rd->tx.buf, rd->tx_size, async);
+
+	/* Reset ppos for next message ... */
+	rd->tx_size = 0;
+
+	return ret ?: count;
+}
+
+static inline __poll_t
+scmi_test_dbg_raw_common_poll(struct file *filp, struct poll_table_struct *wait,
+			      unsigned int idx)
+{
+	unsigned long flags;
+	struct scmi_raw_data *rd = filp->private_data;
+
+	if (!rd->raw->enabled)
+		return 0;
+
+	poll_wait(filp, &rd->raw->wqs[idx], wait);
+
+	spin_lock_irqsave(&rd->raw->msg_q_lock[idx], flags);
+	if (!list_empty(&rd->raw->msg_q[idx])) {
+		spin_unlock_irqrestore(&rd->raw->msg_q_lock[idx], flags);
+		return POLLIN | POLLRDNORM;
+	}
+	spin_unlock_irqrestore(&rd->raw->msg_q_lock[idx], flags);
+
+	return 0;
+}
+
+static ssize_t scmi_dbg_raw_mode_message_read(struct file *filp,
+					      char __user *buf,
+					      size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
+					     SCMI_RAW_REPLY_QUEUE);
+}
+
+static ssize_t scmi_dbg_raw_mode_message_write(struct file *filp,
+					       const char __user *buf,
+					       size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, false);
+}
+
+static __poll_t scmi_dbg_raw_mode_message_poll(struct file *filp,
+					       struct poll_table_struct *wait)
+{
+	return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_REPLY_QUEUE);
+}
+
+static int scmi_dbg_raw_mode_open(struct inode *inode, struct file *filp)
+{
+	struct scmi_raw_mode_info *raw;
+	struct scmi_raw_data *rd;
+
+	if (!inode->i_private)
+		return -ENODEV;
+
+	raw = inode->i_private;
+	rd = kzalloc(sizeof(*rd), GFP_KERNEL);
+	if (!rd)
+		return -ENOMEM;
+
+	rd->rx.len = raw->desc->max_msg_size + sizeof(u32);
+	rd->rx.buf = kzalloc(rd->rx.len, GFP_KERNEL);
+	if (!rd->rx.buf) {
+		kfree(rd);
+		return -ENOMEM;
+	}
+
+	rd->tx.len = raw->desc->max_msg_size + sizeof(u32);
+	rd->tx.buf = kzalloc(rd->tx.len, GFP_KERNEL);
+	if (!rd->tx.buf) {
+		kfree(rd->rx.buf);
+		kfree(rd);
+		return -ENOMEM;
+	}
+
+	rd->raw = raw;
+	filp->private_data = rd;
+
+	return 0;
+}
+
+static int scmi_dbg_raw_mode_release(struct inode *inode, struct file *filp)
+{
+	struct scmi_raw_data *rd = filp->private_data;
+
+	kfree(rd->rx.buf);
+	kfree(rd->tx.buf);
+	kfree(rd);
+
+	return 0;
+}
+
+static ssize_t scmi_dbg_raw_mode_enable_write(struct file *filp,
+					      const char __user *buf,
+					      size_t count, loff_t *ppos)
+{
+	int ret;
+	bool enabled;
+	struct scmi_raw_data *rd = filp->private_data;
+
+	ret = kstrtobool_from_user(buf, count, &enabled);
+	if (ret)
+		return ret;
+
+	ret = scmi_xfer_raw_enable(rd->raw, enabled);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static const struct file_operations scmi_dbg_raw_mode_enable_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.write = scmi_dbg_raw_mode_enable_write,
+	.owner = THIS_MODULE,
+};
+
+static const struct file_operations scmi_dbg_raw_mode_message_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.read = scmi_dbg_raw_mode_message_read,
+	.write = scmi_dbg_raw_mode_message_write,
+	.poll = scmi_dbg_raw_mode_message_poll,
+	.owner = THIS_MODULE,
+};
+
+static ssize_t scmi_dbg_raw_mode_message_async_write(struct file *filp,
+						     const char __user *buf,
+						     size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, true);
+}
+
+static const struct file_operations scmi_dbg_raw_mode_message_async_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.read = scmi_dbg_raw_mode_message_read,
+	.write = scmi_dbg_raw_mode_message_async_write,
+	.poll = scmi_dbg_raw_mode_message_poll,
+	.owner = THIS_MODULE,
+};
+
+static ssize_t scmi_test_dbg_raw_mode_notif_read(struct file *filp,
+						 char __user *buf,
+						 size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
+					     SCMI_RAW_NOTIF_QUEUE);
+}
+
+static __poll_t scmi_test_dbg_raw_mode_notif_poll(struct file *filp,
+						  struct poll_table_struct *wait)
+{
+	return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_NOTIF_QUEUE);
+}
+
+static const struct file_operations scmi_dbg_raw_mode_notification_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.read = scmi_test_dbg_raw_mode_notif_read,
+	.poll = scmi_test_dbg_raw_mode_notif_poll,
+	.owner = THIS_MODULE,
+};
+
+static ssize_t scmi_test_dbg_raw_mode_errors_read(struct file *filp,
+						  char __user *buf,
+						  size_t count, loff_t *ppos)
+{
+	return scmi_dbg_raw_mode_common_read(filp, buf, count, ppos,
+					     SCMI_RAW_ERRS_QUEUE);
+}
+
+static __poll_t scmi_test_dbg_raw_mode_errors_poll(struct file *filp,
+						   struct poll_table_struct *wait)
+{
+	return scmi_test_dbg_raw_common_poll(filp, wait, SCMI_RAW_ERRS_QUEUE);
+}
+
+static const struct file_operations scmi_dbg_raw_mode_errors_fops = {
+	.open = scmi_dbg_raw_mode_open,
+	.release = scmi_dbg_raw_mode_release,
+	.read = scmi_test_dbg_raw_mode_errors_read,
+	.poll = scmi_test_dbg_raw_mode_errors_poll,
+	.owner = THIS_MODULE,
+};
+
+static int scmi_xfer_raw_free_bufs_init(struct scmi_raw_mode_info *raw, int idx)
+{
+	int i;
+	struct scmi_raw_buffer *rb;
+	struct device *dev = raw->cinfo->handle->dev;
+
+	rb = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rb), GFP_KERNEL);
+	if (!rb)
+		return -ENOMEM;
+
+	spin_lock_init(&raw->free_bufs_lock[idx]);
+	INIT_LIST_HEAD(&raw->free_raw_bufs[idx]);
+	for (i = 0; i < raw->tx_max_msg; i++, rb++) {
+		rb->max_len = raw->desc->max_msg_size + sizeof(u32);
+		rb->msg.buf = devm_kzalloc(dev, rb->max_len, GFP_KERNEL);
+		if (!rb->msg.buf)
+			return -ENOMEM;
+		scmi_raw_buffer_put(raw, rb, idx);
+	}
+
+	spin_lock_init(&raw->msg_q_lock[idx]);
+	INIT_LIST_HEAD(&raw->msg_q[idx]);
+	init_waitqueue_head(&raw->wqs[idx]);
+
+	return 0;
+}
+
+static int scmi_xfer_raw_worker_init(struct scmi_raw_mode_info *raw)
+{
+	int i;
+	struct scmi_xfer_raw_waiter *rw;
+	struct device *dev = raw->cinfo->handle->dev;
+
+	rw = devm_kcalloc(dev, raw->tx_max_msg, sizeof(*rw), GFP_KERNEL);
+	if (!rw)
+		return -ENOMEM;
+
+	raw->wrk = devm_kzalloc(dev, sizeof(*raw->wrk), GFP_KERNEL);
+	if (!raw->wrk)
+		return -ENOMEM;
+
+	raw->wrk->wait_wq = alloc_workqueue("scmi-raw-wait-wq-%d",
+					    WQ_UNBOUND | WQ_FREEZABLE |
+					    WQ_HIGHPRI, WQ_SYSFS, 0);
+	if (!raw->wrk->wait_wq)
+		return -ENOMEM;
+
+	raw->wrk->max_tmo = msecs_to_jiffies(raw->desc->max_rx_timeout_ms);
+
+	raw->wrk->raw = raw;
+	mutex_init(&raw->wrk->free_mtx);
+	INIT_LIST_HEAD(&raw->wrk->active_waiters);
+	mutex_init(&raw->wrk->active_mtx);
+	INIT_LIST_HEAD(&raw->wrk->free_waiters);
+	for (i = 0; i < raw->tx_max_msg; i++, rw++) {
+		init_completion(&rw->async_response);
+		scmi_xfer_raw_waiter_put(raw->wrk, rw);
+	}
+	INIT_WORK(&raw->wrk->waiters_work, scmi_xfer_raw_worker);
+	init_waitqueue_head(&raw->wrk->waiters_wq);
+
+	/* kick waiter work */
+	queue_work(raw->wrk->wait_wq, &raw->wrk->waiters_work);
+
+	return 0;
+}
+
+static int scmi_raw_mode_setup(struct scmi_raw_mode_info *raw)
+{
+	int ret;
+	void *gid;
+	struct device *dev = raw->cinfo->handle->dev;
+
+	gid = devres_open_group(dev, NULL, GFP_KERNEL);
+	if (!gid)
+		return -ENOMEM;
+
+	ret = scmi_xfer_raw_free_bufs_init(raw, SCMI_RAW_REPLY_QUEUE);
+	if (ret)
+		goto err;
+
+	ret = scmi_xfer_raw_free_bufs_init(raw, SCMI_RAW_NOTIF_QUEUE);
+	if (ret)
+		goto err;
+
+	ret = scmi_xfer_raw_free_bufs_init(raw, SCMI_RAW_ERRS_QUEUE);
+	if (ret)
+		goto err;
+
+	ret = scmi_xfer_raw_worker_init(raw);
+	if (ret)
+		goto err;
+
+	devres_close_group(dev, gid);
+	raw->gid = gid;
+
+	return 0;
+
+err:
+	devres_release_group(dev, gid);
+	return ret;
+}
+
+void *scmi_raw_mode_init(struct scmi_chan_info *cinfo,
+			 const struct scmi_desc *desc, int tx_max_msg)
+{
+	int ret;
+	struct scmi_raw_mode_info *raw;
+	struct device *dev;
+
+	if (!cinfo || !desc)
+		return ERR_PTR(-EINVAL);
+
+	dev = cinfo->handle->dev;
+	raw = devm_kzalloc(dev, sizeof(*raw), GFP_KERNEL);
+	if (!raw)
+		return ERR_PTR(-ENOMEM);
+
+	raw->cinfo = cinfo;
+	raw->desc = desc;
+	raw->tx_max_msg = tx_max_msg;
+
+	ret = scmi_raw_mode_setup(raw);
+	if (ret) {
+		devm_kfree(dev, raw);
+		return ERR_PTR(ret);
+	}
+
+	raw->dentry = debugfs_create_dir("scmi_raw", NULL);
+	if (IS_ERR(raw->dentry)) {
+		ret = PTR_ERR(raw->dentry);
+		devres_release_group(dev, raw->gid);
+		devm_kfree(dev, raw);
+		return ERR_PTR(ret);
+	}
+
+	debugfs_create_file("enable", 0200, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_enable_fops);
+
+	debugfs_create_u32("transport_rx_timeout_ms", 0400, raw->dentry,
+			   (u32 *)&raw->desc->max_rx_timeout_ms);
+
+	debugfs_create_u32("transport_max_msg_size", 0400, raw->dentry,
+			   (u32 *)&raw->desc->max_msg_size);
+
+	debugfs_create_u32("transport_tx_max_msg", 0400, raw->dentry,
+			   (u32 *)&raw->tx_max_msg);
+
+	debugfs_create_file("message", 0600, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_message_fops);
+
+	debugfs_create_file("message_async", 0600, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_message_async_fops);
+
+	debugfs_create_file("notification", 0400, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_notification_fops);
+
+	debugfs_create_file("errors", 0400, raw->dentry, raw,
+			    &scmi_dbg_raw_mode_errors_fops);
+
+	return raw;
+}
+
+void scmi_raw_mode_cleanup(void *r)
+{
+	struct scmi_raw_mode_info *raw = r;
+
+	if (!raw)
+		return;
+
+	debugfs_remove_recursive(raw->dentry);
+	scmi_xfer_raw_enable(raw, false);
+
+	raw->wrk->wait_wq_shutdown = true;
+	wake_up_interruptible(&raw->wrk->waiters_wq);
+	cancel_work_sync(&raw->wrk->waiters_work);
+	destroy_workqueue(raw->wrk->wait_wq);
+}
diff --git a/drivers/firmware/arm_scmi/raw_mode.h b/drivers/firmware/arm_scmi/raw_mode.h
new file mode 100644
index 000000000000..9988fd31ec91
--- /dev/null
+++ b/drivers/firmware/arm_scmi/raw_mode.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * System Control and Management Interface (SCMI) Message Protocol
+ * Raw mode support header.
+ *
+ * Copyright (C) 2022 ARM Ltd.
+ */
+#ifndef _SCMI_RAW_MODE_H
+#define _SCMI_RAW_MODE_H
+
+#include "common.h"
+
+enum {
+	SCMI_RAW_REPLY_QUEUE,
+	SCMI_RAW_NOTIF_QUEUE,
+	SCMI_RAW_ERRS_QUEUE,
+	SCMI_RAW_MAX_QUEUE
+};
+
+void *scmi_raw_mode_init(struct scmi_chan_info *cinfo,
+			 const struct scmi_desc *desc, int tx_max_msg);
+void scmi_raw_mode_cleanup(void *raw);
+
+void scmi_raw_message_report(void *raw, struct scmi_xfer *xfer,
+			     unsigned int idx);
+void scmi_raw_error_report(void *raw, u32 msg_hdr, void *priv);
+
+#endif /* _SCMI_RAW_MODE_H */
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 6/6] firmware: arm_scmi: Call Raw mode hooks from the core stack
  2022-08-16  7:24 ` Cristian Marussi
@ 2022-08-16  7:24   ` Cristian Marussi
  -1 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Add a few call sites where, if SCMI Raw mode access had been enabled in
Kconfig, the needed SCMI Raw initialization and hooks are called.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 32 ++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 141a140792a5..0068a2ec3b4d 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -23,10 +23,12 @@
 #include <linux/kernel.h>
 #include <linux/ktime.h>
 #include <linux/hashtable.h>
+#include <linux/fs.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
+#include <linux/poll.h>
 #include <linux/processor.h>
 #include <linux/refcount.h>
 #include <linux/slab.h>
@@ -34,6 +36,8 @@
 #include "common.h"
 #include "notify.h"
 
+#include "raw_mode.h"
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/scmi.h>
 
@@ -133,6 +137,7 @@ struct scmi_protocol_instance {
  * @notify_priv: Pointer to private data structure specific to notifications.
  * @node: List head
  * @users: Number of users of this instance
+ * @raw: An opaque reference handle used by SCMI Raw mode.
  */
 struct scmi_info {
 	struct device *dev;
@@ -152,6 +157,7 @@ struct scmi_info {
 	void *notify_priv;
 	struct list_head node;
 	int users;
+	void *raw;
 };
 
 #define handle_to_scmi_info(h)	container_of(h, struct scmi_info, handle)
@@ -678,6 +684,9 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
 static inline void scmi_xfer_command_release(struct scmi_info *info,
 					     struct scmi_xfer *xfer)
 {
+	if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT))
+		scmi_raw_message_report(info->raw, xfer, SCMI_RAW_REPLY_QUEUE);
+
 	atomic_set(&xfer->busy, SCMI_XFER_FREE);
 	__scmi_xfer_put(&info->tx_minfo, xfer);
 }
@@ -744,6 +753,11 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
 			   xfer->hdr.protocol_id, xfer->hdr.seq,
 			   MSG_TYPE_NOTIFICATION);
 
+	if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
+		xfer->hdr.seq = MSG_XTRACT_TOKEN(msg_hdr);
+		scmi_raw_message_report(info->raw, xfer, SCMI_RAW_NOTIF_QUEUE);
+	}
+
 	__scmi_xfer_put(minfo, xfer);
 
 	scmi_clear_channel(info, cinfo);
@@ -757,6 +771,9 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
 
 	xfer = scmi_xfer_command_acquire(cinfo, msg_hdr);
 	if (IS_ERR(xfer)) {
+		if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT))
+			scmi_raw_error_report(info->raw, msg_hdr, priv);
+
 		if (MSG_XTRACT_TYPE(msg_hdr) == MSG_TYPE_DELAYED_RESP)
 			scmi_clear_channel(info, cinfo);
 		return;
@@ -2486,6 +2503,18 @@ static int scmi_probe(struct platform_device *pdev)
 		dev_err(dev,
 			"Transport is not polling capable. Atomic mode not supported.\n");
 
+	if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
+		struct scmi_chan_info *cinfo;
+
+		cinfo = idr_find(&info->tx_idr, SCMI_PROTOCOL_BASE);
+		info->raw = scmi_raw_mode_init(cinfo, info->desc,
+					       info->tx_minfo.max_msg);
+		if (IS_ERR(info->raw)) {
+			dev_err(dev, "Failed to initialize SCMI RAW Mode !\n");
+			info->raw = NULL;
+		}
+	}
+
 	/*
 	 * Trigger SCMI Base protocol initialization.
 	 * It's mandatory and won't be ever released/deinit until the
@@ -2552,6 +2581,9 @@ static int scmi_remove(struct platform_device *pdev)
 	struct scmi_info *info = platform_get_drvdata(pdev);
 	struct device_node *child;
 
+	if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT))
+		scmi_raw_mode_cleanup(info->raw);
+
 	mutex_lock(&scmi_list_mutex);
 	if (info->users)
 		ret = -EBUSY;
-- 
2.32.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [RFC PATCH 6/6] firmware: arm_scmi: Call Raw mode hooks from the core stack
@ 2022-08-16  7:24   ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-16  7:24 UTC (permalink / raw)
  To: linux-kernel, linux-arm-kernel
  Cc: sudeep.holla, james.quinlan, Jonathan.Cameron, f.fainelli,
	etienne.carriere, vincent.guittot, souvik.chakravarty, wleavitt,
	peter.hilber, nicola.mazzucato, tarek.el-sherbiny,
	cristian.marussi

Add a few call sites where, if SCMI Raw mode access had been enabled in
Kconfig, the needed SCMI Raw initialization and hooks are called.

Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/driver.c | 32 ++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 141a140792a5..0068a2ec3b4d 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -23,10 +23,12 @@
 #include <linux/kernel.h>
 #include <linux/ktime.h>
 #include <linux/hashtable.h>
+#include <linux/fs.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
+#include <linux/poll.h>
 #include <linux/processor.h>
 #include <linux/refcount.h>
 #include <linux/slab.h>
@@ -34,6 +36,8 @@
 #include "common.h"
 #include "notify.h"
 
+#include "raw_mode.h"
+
 #define CREATE_TRACE_POINTS
 #include <trace/events/scmi.h>
 
@@ -133,6 +137,7 @@ struct scmi_protocol_instance {
  * @notify_priv: Pointer to private data structure specific to notifications.
  * @node: List head
  * @users: Number of users of this instance
+ * @raw: An opaque reference handle used by SCMI Raw mode.
  */
 struct scmi_info {
 	struct device *dev;
@@ -152,6 +157,7 @@ struct scmi_info {
 	void *notify_priv;
 	struct list_head node;
 	int users;
+	void *raw;
 };
 
 #define handle_to_scmi_info(h)	container_of(h, struct scmi_info, handle)
@@ -678,6 +684,9 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
 static inline void scmi_xfer_command_release(struct scmi_info *info,
 					     struct scmi_xfer *xfer)
 {
+	if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT))
+		scmi_raw_message_report(info->raw, xfer, SCMI_RAW_REPLY_QUEUE);
+
 	atomic_set(&xfer->busy, SCMI_XFER_FREE);
 	__scmi_xfer_put(&info->tx_minfo, xfer);
 }
@@ -744,6 +753,11 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
 			   xfer->hdr.protocol_id, xfer->hdr.seq,
 			   MSG_TYPE_NOTIFICATION);
 
+	if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
+		xfer->hdr.seq = MSG_XTRACT_TOKEN(msg_hdr);
+		scmi_raw_message_report(info->raw, xfer, SCMI_RAW_NOTIF_QUEUE);
+	}
+
 	__scmi_xfer_put(minfo, xfer);
 
 	scmi_clear_channel(info, cinfo);
@@ -757,6 +771,9 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
 
 	xfer = scmi_xfer_command_acquire(cinfo, msg_hdr);
 	if (IS_ERR(xfer)) {
+		if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT))
+			scmi_raw_error_report(info->raw, msg_hdr, priv);
+
 		if (MSG_XTRACT_TYPE(msg_hdr) == MSG_TYPE_DELAYED_RESP)
 			scmi_clear_channel(info, cinfo);
 		return;
@@ -2486,6 +2503,18 @@ static int scmi_probe(struct platform_device *pdev)
 		dev_err(dev,
 			"Transport is not polling capable. Atomic mode not supported.\n");
 
+	if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
+		struct scmi_chan_info *cinfo;
+
+		cinfo = idr_find(&info->tx_idr, SCMI_PROTOCOL_BASE);
+		info->raw = scmi_raw_mode_init(cinfo, info->desc,
+					       info->tx_minfo.max_msg);
+		if (IS_ERR(info->raw)) {
+			dev_err(dev, "Failed to initialize SCMI RAW Mode !\n");
+			info->raw = NULL;
+		}
+	}
+
 	/*
 	 * Trigger SCMI Base protocol initialization.
 	 * It's mandatory and won't be ever released/deinit until the
@@ -2552,6 +2581,9 @@ static int scmi_remove(struct platform_device *pdev)
 	struct scmi_info *info = platform_get_drvdata(pdev);
 	struct device_node *child;
 
+	if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT))
+		scmi_raw_mode_cleanup(info->raw);
+
 	mutex_lock(&scmi_list_mutex);
 	if (info->users)
 		ret = -EBUSY;
-- 
2.32.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
  2022-08-16  7:24   ` Cristian Marussi
@ 2022-08-16 18:03     ` Mark Brown
  -1 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2022-08-16 18:03 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, james.quinlan,
	Jonathan.Cameron, f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty, wleavitt, peter.hilber, nicola.mazzucato,
	tarek.el-sherbiny

[-- Attachment #1: Type: text/plain, Size: 830 bytes --]

On Tue, Aug 16, 2022 at 08:24:49AM +0100, Cristian Marussi wrote:
> Add SCMI Raw mode support which exposes a userspace interface rooted under
> /sys/kernel/debug/scmi_raw.

> Raw mode can be enabled/disabled at runtime via ./scmi_raw/enable.
> Once enabled, all the regular SCMI drivers activity is inhibited and a
> userspace application can then inject and read back bare SCMI messages
> writing and reading to/from ./scmi_raw/message* entries.

Is there a strong reason to have the runtime enable/disable?  Given that
this is going to be used in special kernel builds rather than something
people have as standard it feels like the transition to/from raw mode is
opening up a set of extra use cases that wouldn't normally come up for
the SCMI drivers (especially if the testing ends up leaving the firmware
in a weird state).

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
@ 2022-08-16 18:03     ` Mark Brown
  0 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2022-08-16 18:03 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, james.quinlan,
	Jonathan.Cameron, f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty, wleavitt, peter.hilber, nicola.mazzucato,
	tarek.el-sherbiny


[-- Attachment #1.1: Type: text/plain, Size: 830 bytes --]

On Tue, Aug 16, 2022 at 08:24:49AM +0100, Cristian Marussi wrote:
> Add SCMI Raw mode support which exposes a userspace interface rooted under
> /sys/kernel/debug/scmi_raw.

> Raw mode can be enabled/disabled at runtime via ./scmi_raw/enable.
> Once enabled, all the regular SCMI drivers activity is inhibited and a
> userspace application can then inject and read back bare SCMI messages
> writing and reading to/from ./scmi_raw/message* entries.

Is there a strong reason to have the runtime enable/disable?  Given that
this is going to be used in special kernel builds rather than something
people have as standard it feels like the transition to/from raw mode is
opening up a set of extra use cases that wouldn't normally come up for
the SCMI drivers (especially if the testing ends up leaving the firmware
in a weird state).

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
  2022-08-16 18:03     ` Mark Brown
@ 2022-08-17  8:38       ` Cristian Marussi
  -1 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-17  8:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, james.quinlan,
	Jonathan.Cameron, f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty, wleavitt, peter.hilber, nicola.mazzucato,
	tarek.el-sherbiny

On Tue, Aug 16, 2022 at 07:03:21PM +0100, Mark Brown wrote:
> On Tue, Aug 16, 2022 at 08:24:49AM +0100, Cristian Marussi wrote:
> > Add SCMI Raw mode support which exposes a userspace interface rooted under
> > /sys/kernel/debug/scmi_raw.

Hi Mark,

thanks for having a look.

> 
> > Raw mode can be enabled/disabled at runtime via ./scmi_raw/enable.
> > Once enabled, all the regular SCMI drivers activity is inhibited and a
> > userspace application can then inject and read back bare SCMI messages
> > writing and reading to/from ./scmi_raw/message* entries.
> 
> Is there a strong reason to have the runtime enable/disable?  Given that
> this is going to be used in special kernel builds rather than something
> people have as standard it feels like the transition to/from raw mode is
> opening up a set of extra use cases that wouldn't normally come up for
> the SCMI drivers (especially if the testing ends up leaving the firmware
> in a weird state).

The rationale for this dynamic runtime switch was to try to have a
system that can be configured for SCMI FW testing BUT not necessarily
specifically only for such SCMI tests...IOW a system that can be used in
CI to run a number of other test suites (while in normal mode) and then
switched to raw mode only when SCMI compliance tests are to be run.

Indeed, the enable/disable thing is the main critical point of this RFC
since especially the disable-and-back-to-normal transition proved to be
potentially problematic...i.e. the system generally works in my setup but
it cannot be guaranteed to really bring back the system in a fully
functional state depending on how complex the driver stack is
(..beside the potential FW final weird state issue as you rightly
pointed out)

...moreover at the end the whole disable and go-back-to-normal really
makes little sense in a typical CI scenario where anyway the system
under test is most probably rebooted between runs of different test
suites, so we really do not care about any weird final state probably.

I, nonetheless, posted this RFC with this such support, at first to have
some general feedback, BUT also because I'm still anyway wondering if it
would not be worth to keep at least the capability to only enable it at
run-time (dropping the disable-back-to-normal feat), because this would
enable to build an image which includes this SCMI Raw support, which is
default disabled, and that can at will enabled at runtime only on selected
runs, so that this same test-image could still be used in a number of
different CI test-runs (keeping raw mode disabled and silent) but also then
used for a specific SCMI testing run that would eventually enable it.

If this is not worth really I can just drop the whole runtime switch thing
and stick to the more conservative approach of having a dedicated image
for this kind of SCMI FW testing: a system that once configured at compile
time with this, it just cannot use at all the regular SCMI stack (...which
does NOT necessarily prevent the system to boot and be used for other non
SCMI testing indeed...it would just be probably slower...)

Any thought ? 

Thanks,
Cristian

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
@ 2022-08-17  8:38       ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-17  8:38 UTC (permalink / raw)
  To: Mark Brown
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, james.quinlan,
	Jonathan.Cameron, f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty, wleavitt, peter.hilber, nicola.mazzucato,
	tarek.el-sherbiny

On Tue, Aug 16, 2022 at 07:03:21PM +0100, Mark Brown wrote:
> On Tue, Aug 16, 2022 at 08:24:49AM +0100, Cristian Marussi wrote:
> > Add SCMI Raw mode support which exposes a userspace interface rooted under
> > /sys/kernel/debug/scmi_raw.

Hi Mark,

thanks for having a look.

> 
> > Raw mode can be enabled/disabled at runtime via ./scmi_raw/enable.
> > Once enabled, all the regular SCMI drivers activity is inhibited and a
> > userspace application can then inject and read back bare SCMI messages
> > writing and reading to/from ./scmi_raw/message* entries.
> 
> Is there a strong reason to have the runtime enable/disable?  Given that
> this is going to be used in special kernel builds rather than something
> people have as standard it feels like the transition to/from raw mode is
> opening up a set of extra use cases that wouldn't normally come up for
> the SCMI drivers (especially if the testing ends up leaving the firmware
> in a weird state).

The rationale for this dynamic runtime switch was to try to have a
system that can be configured for SCMI FW testing BUT not necessarily
specifically only for such SCMI tests...IOW a system that can be used in
CI to run a number of other test suites (while in normal mode) and then
switched to raw mode only when SCMI compliance tests are to be run.

Indeed, the enable/disable thing is the main critical point of this RFC
since especially the disable-and-back-to-normal transition proved to be
potentially problematic...i.e. the system generally works in my setup but
it cannot be guaranteed to really bring back the system in a fully
functional state depending on how complex the driver stack is
(..beside the potential FW final weird state issue as you rightly
pointed out)

...moreover at the end the whole disable and go-back-to-normal really
makes little sense in a typical CI scenario where anyway the system
under test is most probably rebooted between runs of different test
suites, so we really do not care about any weird final state probably.

I, nonetheless, posted this RFC with this such support, at first to have
some general feedback, BUT also because I'm still anyway wondering if it
would not be worth to keep at least the capability to only enable it at
run-time (dropping the disable-back-to-normal feat), because this would
enable to build an image which includes this SCMI Raw support, which is
default disabled, and that can at will enabled at runtime only on selected
runs, so that this same test-image could still be used in a number of
different CI test-runs (keeping raw mode disabled and silent) but also then
used for a specific SCMI testing run that would eventually enable it.

If this is not worth really I can just drop the whole runtime switch thing
and stick to the more conservative approach of having a dedicated image
for this kind of SCMI FW testing: a system that once configured at compile
time with this, it just cannot use at all the regular SCMI stack (...which
does NOT necessarily prevent the system to boot and be used for other non
SCMI testing indeed...it would just be probably slower...)

Any thought ? 

Thanks,
Cristian

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
  2022-08-17  8:38       ` Cristian Marussi
@ 2022-08-17 13:42         ` Mark Brown
  -1 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2022-08-17 13:42 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, james.quinlan,
	Jonathan.Cameron, f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty, wleavitt, peter.hilber, nicola.mazzucato,
	tarek.el-sherbiny

[-- Attachment #1: Type: text/plain, Size: 1786 bytes --]

On Wed, Aug 17, 2022 at 09:38:57AM +0100, Cristian Marussi wrote:

> ...moreover at the end the whole disable and go-back-to-normal really
> makes little sense in a typical CI scenario where anyway the system
> under test is most probably rebooted between runs of different test
> suites, so we really do not care about any weird final state probably.

> I, nonetheless, posted this RFC with this such support, at first to have
> some general feedback, BUT also because I'm still anyway wondering if it
> would not be worth to keep at least the capability to only enable it at
> run-time (dropping the disable-back-to-normal feat), because this would
> enable to build an image which includes this SCMI Raw support, which is
> default disabled, and that can at will enabled at runtime only on selected
> runs, so that this same test-image could still be used in a number of
> different CI test-runs (keeping raw mode disabled and silent) but also then
> used for a specific SCMI testing run that would eventually enable it.

The enable usecase does indeed make more sense, though I'd still worry
about other code having problems with the SCMI support getting
hotplugged out from underneath it since that isn't a thing that happens
in practical systems.  For example the archrandom code is going to get
confused since it probes once to see if SMCCC TRNG support is available
and if it's present sets a flag which it assumes will be true for the
rest of system runtime.  I don't entirely know how the image build costs
play off here for the people who'd actually be running these tests, but
my instinct is that the extra kernel build isn't really much in the
grand scheme of things compared to shaking out the consequences of a
runtime switch and the costs of actually running the tests.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
@ 2022-08-17 13:42         ` Mark Brown
  0 siblings, 0 replies; 22+ messages in thread
From: Mark Brown @ 2022-08-17 13:42 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, james.quinlan,
	Jonathan.Cameron, f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty, wleavitt, peter.hilber, nicola.mazzucato,
	tarek.el-sherbiny


[-- Attachment #1.1: Type: text/plain, Size: 1786 bytes --]

On Wed, Aug 17, 2022 at 09:38:57AM +0100, Cristian Marussi wrote:

> ...moreover at the end the whole disable and go-back-to-normal really
> makes little sense in a typical CI scenario where anyway the system
> under test is most probably rebooted between runs of different test
> suites, so we really do not care about any weird final state probably.

> I, nonetheless, posted this RFC with this such support, at first to have
> some general feedback, BUT also because I'm still anyway wondering if it
> would not be worth to keep at least the capability to only enable it at
> run-time (dropping the disable-back-to-normal feat), because this would
> enable to build an image which includes this SCMI Raw support, which is
> default disabled, and that can at will enabled at runtime only on selected
> runs, so that this same test-image could still be used in a number of
> different CI test-runs (keeping raw mode disabled and silent) but also then
> used for a specific SCMI testing run that would eventually enable it.

The enable usecase does indeed make more sense, though I'd still worry
about other code having problems with the SCMI support getting
hotplugged out from underneath it since that isn't a thing that happens
in practical systems.  For example the archrandom code is going to get
confused since it probes once to see if SMCCC TRNG support is available
and if it's present sets a flag which it assumes will be true for the
rest of system runtime.  I don't entirely know how the image build costs
play off here for the people who'd actually be running these tests, but
my instinct is that the extra kernel build isn't really much in the
grand scheme of things compared to shaking out the consequences of a
runtime switch and the costs of actually running the tests.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
  2022-08-17 13:42         ` Mark Brown
@ 2022-08-17 14:21           ` Cristian Marussi
  -1 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-17 14:21 UTC (permalink / raw)
  To: Mark Brown
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, james.quinlan,
	Jonathan.Cameron, f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty, wleavitt, peter.hilber, nicola.mazzucato,
	tarek.el-sherbiny

On Wed, Aug 17, 2022 at 02:42:40PM +0100, Mark Brown wrote:
> On Wed, Aug 17, 2022 at 09:38:57AM +0100, Cristian Marussi wrote:
> 
> > ...moreover at the end the whole disable and go-back-to-normal really
> > makes little sense in a typical CI scenario where anyway the system
> > under test is most probably rebooted between runs of different test
> > suites, so we really do not care about any weird final state probably.
> 
> > I, nonetheless, posted this RFC with this such support, at first to have
> > some general feedback, BUT also because I'm still anyway wondering if it
> > would not be worth to keep at least the capability to only enable it at
> > run-time (dropping the disable-back-to-normal feat), because this would
> > enable to build an image which includes this SCMI Raw support, which is
> > default disabled, and that can at will enabled at runtime only on selected
> > runs, so that this same test-image could still be used in a number of
> > different CI test-runs (keeping raw mode disabled and silent) but also then
> > used for a specific SCMI testing run that would eventually enable it.
> 
> The enable usecase does indeed make more sense, though I'd still worry
> about other code having problems with the SCMI support getting
> hotplugged out from underneath it since that isn't a thing that happens
> in practical systems.  For example the archrandom code is going to get
> confused since it probes once to see if SMCCC TRNG support is available
> and if it's present sets a flag which it assumes will be true for the
> rest of system runtime.  I don't entirely know how the image build costs
> play off here for the people who'd actually be running these tests, but
> my instinct is that the extra kernel build isn't really much in the
> grand scheme of things compared to shaking out the consequences of a
> runtime switch and the costs of actually running the tests.

Indeed Sudeep had the same concerns and when I tested this with a dummy
driver using an SCMI clock I could see that the disappearing clock was
seemingly handled safely by the clock framework, but this is just one
of the possibly involved framework, I cannot be so sure about all the
others and any future frmk that will have to interact with this dynamic
unplugging...so at the end probably is not worth the effort as you are
saying...I'll most probably drop this next....

Thanks a lot for your feedback.
Cristian


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support
@ 2022-08-17 14:21           ` Cristian Marussi
  0 siblings, 0 replies; 22+ messages in thread
From: Cristian Marussi @ 2022-08-17 14:21 UTC (permalink / raw)
  To: Mark Brown
  Cc: linux-kernel, linux-arm-kernel, sudeep.holla, james.quinlan,
	Jonathan.Cameron, f.fainelli, etienne.carriere, vincent.guittot,
	souvik.chakravarty, wleavitt, peter.hilber, nicola.mazzucato,
	tarek.el-sherbiny

On Wed, Aug 17, 2022 at 02:42:40PM +0100, Mark Brown wrote:
> On Wed, Aug 17, 2022 at 09:38:57AM +0100, Cristian Marussi wrote:
> 
> > ...moreover at the end the whole disable and go-back-to-normal really
> > makes little sense in a typical CI scenario where anyway the system
> > under test is most probably rebooted between runs of different test
> > suites, so we really do not care about any weird final state probably.
> 
> > I, nonetheless, posted this RFC with this such support, at first to have
> > some general feedback, BUT also because I'm still anyway wondering if it
> > would not be worth to keep at least the capability to only enable it at
> > run-time (dropping the disable-back-to-normal feat), because this would
> > enable to build an image which includes this SCMI Raw support, which is
> > default disabled, and that can at will enabled at runtime only on selected
> > runs, so that this same test-image could still be used in a number of
> > different CI test-runs (keeping raw mode disabled and silent) but also then
> > used for a specific SCMI testing run that would eventually enable it.
> 
> The enable usecase does indeed make more sense, though I'd still worry
> about other code having problems with the SCMI support getting
> hotplugged out from underneath it since that isn't a thing that happens
> in practical systems.  For example the archrandom code is going to get
> confused since it probes once to see if SMCCC TRNG support is available
> and if it's present sets a flag which it assumes will be true for the
> rest of system runtime.  I don't entirely know how the image build costs
> play off here for the people who'd actually be running these tests, but
> my instinct is that the extra kernel build isn't really much in the
> grand scheme of things compared to shaking out the consequences of a
> runtime switch and the costs of actually running the tests.

Indeed Sudeep had the same concerns and when I tested this with a dummy
driver using an SCMI clock I could see that the disappearing clock was
seemingly handled safely by the clock framework, but this is just one
of the possibly involved framework, I cannot be so sure about all the
others and any future frmk that will have to interact with this dynamic
unplugging...so at the end probably is not worth the effort as you are
saying...I'll most probably drop this next....

Thanks a lot for your feedback.
Cristian


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2022-08-17 14:22 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-16  7:24 [RFC PATCH 0/6] Introduce a unified API for SCMI Server testing Cristian Marussi
2022-08-16  7:24 ` Cristian Marussi
2022-08-16  7:24 ` [RFC PATCH 1/6] firmware: arm_scmi: Refactor xfer in-flight registration routines Cristian Marussi
2022-08-16  7:24   ` Cristian Marussi
2022-08-16  7:24 ` [RFC PATCH 2/6] firmware: arm_scmi: Add bus helpers to enter raw mode Cristian Marussi
2022-08-16  7:24   ` Cristian Marussi
2022-08-16  7:24 ` [RFC PATCH 3/6] firmware: arm_scmi: Add xfer raw helpers Cristian Marussi
2022-08-16  7:24   ` Cristian Marussi
2022-08-16  7:24 ` [RFC PATCH 4/6] firmware: arm_scmi: Move errors defs and code to common.h Cristian Marussi
2022-08-16  7:24   ` Cristian Marussi
2022-08-16  7:24 ` [RFC PATCH 5/6] firmware: arm_scmi: Add raw transmission support Cristian Marussi
2022-08-16  7:24   ` Cristian Marussi
2022-08-16 18:03   ` Mark Brown
2022-08-16 18:03     ` Mark Brown
2022-08-17  8:38     ` Cristian Marussi
2022-08-17  8:38       ` Cristian Marussi
2022-08-17 13:42       ` Mark Brown
2022-08-17 13:42         ` Mark Brown
2022-08-17 14:21         ` Cristian Marussi
2022-08-17 14:21           ` Cristian Marussi
2022-08-16  7:24 ` [RFC PATCH 6/6] firmware: arm_scmi: Call Raw mode hooks from the core stack Cristian Marussi
2022-08-16  7:24   ` Cristian Marussi

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.