Commit b1a1b152 authored by Sudeep Holla's avatar Sudeep Holla
Browse files

Merge tag 'scmi-updates-5.17' of...

Merge tag 'scmi-updates-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into for-next/scmi

Arm SCMI firmware interface updates for v5.17

Couple of main additions:
- Support for OPTEE based SCMI transport to enable using SCMI service
  provided by OPTEE on some platforms
- Support for atomic SCMI transports which enables few SCMI transactions
  to be completed in atomic context. This involves other refactoring work
  associated with it. It also marks SMC and OPTEE as atomic transport as
  the commands are completed once the return

Other changes involves some trace and log enhancements and a miscellaneous
bug fix.

* tag 'scmi-updates-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: arm_scmi: Add new parameter to mark_txdone
  firmware: arm_scmi: Add atomic mode support to smc transport
  firmware: arm_scmi: Add support for atomic transports
  firmware: arm_scmi: Make optee support sync_cmds_completed_on_ret
  firmware: arm_scmi: Make smc support sync_cmds_completed_on_ret
  firmware: arm_scmi: Add sync_cmds_completed_on_ret transport flag
  firmware: arm_scmi: Make smc transport use common completions
  firmware: arm_scmi: Add configurable polling mode for transports
  firmware: arm_scmi: Use new trace event scmi_xfer_response_wait
  include: trace: Add new scmi_xfer_response_wait event
  firmware: arm_scmi: Refactor message response path
  firmware: arm_scmi: Set polling timeout to max_rx_timeout_ms
  firmware: arm_scmi: Perform earlier cinfo lookup call in do_xfer
  firmware: arm_scmi: optee: Drop the support for the OPTEE shared dynamic buffer
  firmware: arm_scmi: optee: Fix missing mutex_init()
  firmware: arm_scmi: Make virtio Version_1 compliance optional
  firmware: arm_scmi: Add optee transport
  dt-bindings: arm: Add OP-TEE transport for SCMI
  firmware: arm_scmi: Review some virtio log messages
parents e783362e 94d0cd1d
Loading
Loading
Loading
Loading
+65 −0
Original line number Diff line number Diff line
@@ -38,6 +38,9 @@ properties:
                     The virtio transport only supports a single device.
        items:
          - const: arm,scmi-virtio
      - description: SCMI compliant firmware with OP-TEE transport
        items:
          - const: linaro,scmi-optee

  interrupts:
    description:
@@ -83,6 +86,11 @@ properties:
    description:
      SMC id required when using smc or hvc transports

  linaro,optee-channel-id:
    $ref: /schemas/types.yaml#/definitions/uint32
    description:
      Channel specifier required when using OP-TEE transport.

  protocol@11:
    type: object
    properties:
@@ -195,6 +203,12 @@ patternProperties:
        minItems: 1
        maxItems: 2

      linaro,optee-channel-id:
        $ref: /schemas/types.yaml#/definitions/uint32
        description:
          Channel specifier required when using OP-TEE transport and
          protocol has a dedicated communication channel.

    required:
      - reg

@@ -226,6 +240,16 @@ else:
      - arm,smc-id
      - shmem

  else:
    if:
      properties:
        compatible:
          contains:
            const: linaro,scmi-optee
    then:
      required:
        - linaro,optee-channel-id

examples:
  - |
    firmware {
@@ -340,7 +364,48 @@ examples:
                reg = <0x11>;
                #power-domain-cells = <1>;
            };
        };
    };

  - |
    firmware {
        scmi {
            compatible = "linaro,scmi-optee";
            linaro,optee-channel-id = <0>;

            #address-cells = <1>;
            #size-cells = <0>;

            scmi_dvfs1: protocol@13 {
                reg = <0x13>;
                linaro,optee-channel-id = <1>;
                shmem = <&cpu_optee_lpri0>;
                #clock-cells = <1>;
            };

            scmi_clk0: protocol@14 {
                reg = <0x14>;
                #clock-cells = <1>;
            };
        };
    };

    soc {
        #address-cells = <2>;
        #size-cells = <2>;

        sram@51000000 {
            compatible = "mmio-sram";
            reg = <0x0 0x51000000 0x0 0x10000>;

            #address-cells = <1>;
            #size-cells = <1>;
            ranges = <0 0x0 0x51000000 0x10000>;

            cpu_optee_lpri0: optee-sram-section@0 {
                compatible = "arm,scmi-shmem";
                reg = <0x0 0x80>;
            };
        };
    };

+41 −0
Original line number Diff line number Diff line
@@ -54,6 +54,18 @@ config ARM_SCMI_TRANSPORT_MAILBOX
	  If you want the ARM SCMI PROTOCOL stack to include support for a
	  transport based on mailboxes, answer Y.

config ARM_SCMI_TRANSPORT_OPTEE
	bool "SCMI transport based on OP-TEE service"
	depends on OPTEE=y || OPTEE=ARM_SCMI_PROTOCOL
	select ARM_SCMI_HAVE_TRANSPORT
	select ARM_SCMI_HAVE_SHMEM
	default y
	help
	  This enables the OP-TEE service based transport for SCMI.

	  If you want the ARM SCMI PROTOCOL stack to include support for a
	  transport based on OP-TEE SCMI service, answer Y.

config ARM_SCMI_TRANSPORT_SMC
	bool "SCMI transport based on SMC"
	depends on HAVE_ARM_SMCCC_DISCOVERY
@@ -66,6 +78,20 @@ config ARM_SCMI_TRANSPORT_SMC
	  If you want the ARM SCMI PROTOCOL stack to include support for a
	  transport based on SMC, answer Y.

config ARM_SCMI_TRANSPORT_SMC_ATOMIC_ENABLE
	bool "Enable atomic mode support for SCMI SMC transport"
	depends on ARM_SCMI_TRANSPORT_SMC
	help
	  Enable support of atomic operation for SCMI SMC based transport.

	  If you want the SCMI SMC based transport to operate in atomic
	  mode, avoiding any kind of sleeping behaviour for selected
	  transactions on the TX path, answer Y.
	  Enabling atomic mode operations allows any SCMI driver using this
	  transport to optionally ask for atomic SCMI transactions and operate
	  in atomic context too, at the price of using a number of busy-waiting
	  primitives all over instead. If unsure say N.

config ARM_SCMI_TRANSPORT_VIRTIO
	bool "SCMI transport based on VirtIO"
	depends on VIRTIO=y || VIRTIO=ARM_SCMI_PROTOCOL
@@ -77,6 +103,21 @@ config ARM_SCMI_TRANSPORT_VIRTIO
	  If you want the ARM SCMI PROTOCOL stack to include support for a
	  transport based on VirtIO, answer Y.

config ARM_SCMI_TRANSPORT_VIRTIO_VERSION1_COMPLIANCE
	bool "SCMI VirtIO transport Version 1 compliance"
	depends on ARM_SCMI_TRANSPORT_VIRTIO
	default y
	help
	  This enforces strict compliance with VirtIO Version 1 specification.

	  If you want the ARM SCMI VirtIO transport layer to refuse to work
	  with Legacy VirtIO backends and instead support only VirtIO Version 1
	  devices (or above), answer Y.

	  If you want instead to support also old Legacy VirtIO backends (like
	  the ones implemented by kvmtool) and let the core Kernel VirtIO layer
	  take care of the needed conversions, say N.

endif #ARM_SCMI_PROTOCOL

config ARM_SCMI_POWER_DOMAIN
+1 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += mailbox.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += smc.o
scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += virtio.o
scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += optee.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o
scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
		    $(scmi-transport-y)
+25 −1
Original line number Diff line number Diff line
@@ -339,11 +339,16 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
 * @dev: Reference to device in the SCMI hierarchy corresponding to this
 *	 channel
 * @handle: Pointer to SCMI entity handle
 * @no_completion_irq: Flag to indicate that this channel has no completion
 *		       interrupt mechanism for synchronous commands.
 *		       This can be dynamically set by transports at run-time
 *		       inside their provided .chan_setup().
 * @transport_info: Transport layer related information
 */
struct scmi_chan_info {
	struct device *dev;
	struct scmi_handle *handle;
	bool no_completion_irq;
	void *transport_info;
};

@@ -373,7 +378,8 @@ struct scmi_transport_ops {
	unsigned int (*get_max_msg)(struct scmi_chan_info *base_cinfo);
	int (*send_message)(struct scmi_chan_info *cinfo,
			    struct scmi_xfer *xfer);
	void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret);
	void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret,
			    struct scmi_xfer *xfer);
	void (*fetch_response)(struct scmi_chan_info *cinfo,
			       struct scmi_xfer *xfer);
	void (*fetch_notification)(struct scmi_chan_info *cinfo,
@@ -402,6 +408,18 @@ struct scmi_device *scmi_child_dev_find(struct device *parent,
 *	be pending simultaneously in the system. May be overridden by the
 *	get_max_msg op.
 * @max_msg_size: Maximum size of data per message that can be handled.
 * @force_polling: Flag to force this whole transport to use SCMI core polling
 *		   mechanism instead of completion interrupts even if available.
 * @sync_cmds_completed_on_ret: Flag to indicate that the transport assures
 *				synchronous-command messages are atomically
 *				completed on .send_message: no need to poll
 *				actively waiting for a response.
 *				Used by core internally only when polling is
 *				selected as a waiting for reply method: i.e.
 *				if a completion irq was found use that anyway.
 * @atomic_enabled: Flag to indicate that this transport, which is assured not
 *		    to sleep anywhere on the TX path, can be used in atomic mode
 *		    when requested.
 */
struct scmi_desc {
	int (*transport_init)(void);
@@ -410,6 +428,9 @@ struct scmi_desc {
	int max_rx_timeout_ms;
	int max_msg;
	int max_msg_size;
	const bool force_polling;
	const bool sync_cmds_completed_on_ret;
	const bool atomic_enabled;
};

#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
@@ -421,6 +442,9 @@ extern const struct scmi_desc scmi_smc_desc;
#ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO
extern const struct scmi_desc scmi_virtio_desc;
#endif
#ifdef CONFIG_ARM_SCMI_TRANSPORT_OPTEE
extern const struct scmi_desc scmi_optee_desc;
#endif

void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv);
void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
+166 −41
Original line number Diff line number Diff line
@@ -609,6 +609,25 @@ static inline void scmi_clear_channel(struct scmi_info *info,
		info->desc->ops->clear_channel(cinfo);
}

static inline bool is_polling_required(struct scmi_chan_info *cinfo,
				       struct scmi_info *info)
{
	return cinfo->no_completion_irq || info->desc->force_polling;
}

static inline bool is_transport_polling_capable(struct scmi_info *info)
{
	return info->desc->ops->poll_done ||
		info->desc->sync_cmds_completed_on_ret;
}

static inline bool is_polling_enabled(struct scmi_chan_info *cinfo,
				      struct scmi_info *info)
{
	return is_polling_required(cinfo, info) &&
		is_transport_polling_capable(info);
}

static void scmi_handle_notification(struct scmi_chan_info *cinfo,
				     u32 msg_hdr, void *priv)
{
@@ -724,8 +743,6 @@ static void xfer_put(const struct scmi_protocol_handle *ph,
	__scmi_xfer_put(&info->tx_minfo, xfer);
}

#define SCMI_MAX_POLL_TO_NS	(100 * NSEC_PER_USEC)

static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo,
				      struct scmi_xfer *xfer, ktime_t stop)
{
@@ -740,6 +757,79 @@ static bool scmi_xfer_done_no_timeout(struct scmi_chan_info *cinfo,
	       ktime_after(ktime_get(), stop);
}

/**
 * scmi_wait_for_message_response  - An helper to group all the possible ways of
 * waiting for a synchronous message response.
 *
 * @cinfo: SCMI channel info
 * @xfer: Reference to the transfer being waited for.
 *
 * Chooses waiting strategy (sleep-waiting vs busy-waiting) depending on
 * configuration flags like xfer->hdr.poll_completion.
 *
 * Return: 0 on Success, error otherwise.
 */
static int scmi_wait_for_message_response(struct scmi_chan_info *cinfo,
					  struct scmi_xfer *xfer)
{
	struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
	struct device *dev = info->dev;
	int ret = 0, timeout_ms = info->desc->max_rx_timeout_ms;

	trace_scmi_xfer_response_wait(xfer->transfer_id, xfer->hdr.id,
				      xfer->hdr.protocol_id, xfer->hdr.seq,
				      timeout_ms,
				      xfer->hdr.poll_completion);

	if (xfer->hdr.poll_completion) {
		/*
		 * Real polling is needed only if transport has NOT declared
		 * itself to support synchronous commands replies.
		 */
		if (!info->desc->sync_cmds_completed_on_ret) {
			/*
			 * Poll on xfer using transport provided .poll_done();
			 * assumes no completion interrupt was available.
			 */
			ktime_t stop = ktime_add_ms(ktime_get(), timeout_ms);

			spin_until_cond(scmi_xfer_done_no_timeout(cinfo,
								  xfer, stop));
			if (ktime_after(ktime_get(), stop)) {
				dev_err(dev,
					"timed out in resp(caller: %pS) - polling\n",
					(void *)_RET_IP_);
				ret = -ETIMEDOUT;
			}
		}

		if (!ret) {
			unsigned long flags;

			/*
			 * Do not fetch_response if an out-of-order delayed
			 * response is being processed.
			 */
			spin_lock_irqsave(&xfer->lock, flags);
			if (xfer->state == SCMI_XFER_SENT_OK) {
				info->desc->ops->fetch_response(cinfo, xfer);
				xfer->state = SCMI_XFER_RESP_OK;
			}
			spin_unlock_irqrestore(&xfer->lock, flags);
		}
	} else {
		/* And we wait for the response. */
		if (!wait_for_completion_timeout(&xfer->done,
						 msecs_to_jiffies(timeout_ms))) {
			dev_err(dev, "timed out in resp(caller: %pS)\n",
				(void *)_RET_IP_);
			ret = -ETIMEDOUT;
		}
	}

	return ret;
}

/**
 * do_xfer() - Do one transfer
 *
@@ -754,18 +844,26 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
		   struct scmi_xfer *xfer)
{
	int ret;
	int timeout;
	const struct scmi_protocol_instance *pi = ph_to_pi(ph);
	struct scmi_info *info = handle_to_scmi_info(pi->handle);
	struct device *dev = info->dev;
	struct scmi_chan_info *cinfo;

	if (xfer->hdr.poll_completion && !info->desc->ops->poll_done) {
	/* Check for polling request on custom command xfers at first */
	if (xfer->hdr.poll_completion && !is_transport_polling_capable(info)) {
		dev_warn_once(dev,
			      "Polling mode is not supported by transport.\n");
		return -EINVAL;
	}

	cinfo = idr_find(&info->tx_idr, pi->proto->id);
	if (unlikely(!cinfo))
		return -EINVAL;

	/* True ONLY if also supported by transport. */
	if (is_polling_enabled(cinfo, info))
		xfer->hdr.poll_completion = true;

	/*
	 * Initialise protocol id now from protocol handle to avoid it being
	 * overridden by mistake (or malice) by the protocol code mangling with
@@ -774,10 +872,6 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
	xfer->hdr.protocol_id = pi->proto->id;
	reinit_completion(&xfer->done);

	cinfo = idr_find(&info->tx_idr, xfer->hdr.protocol_id);
	if (unlikely(!cinfo))
		return -EINVAL;

	trace_scmi_xfer_begin(xfer->transfer_id, xfer->hdr.id,
			      xfer->hdr.protocol_id, xfer->hdr.seq,
			      xfer->hdr.poll_completion);
@@ -798,41 +892,12 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
		return ret;
	}

	if (xfer->hdr.poll_completion) {
		ktime_t stop = ktime_add_ns(ktime_get(), SCMI_MAX_POLL_TO_NS);

		spin_until_cond(scmi_xfer_done_no_timeout(cinfo, xfer, stop));
		if (ktime_before(ktime_get(), stop)) {
			unsigned long flags;

			/*
			 * Do not fetch_response if an out-of-order delayed
			 * response is being processed.
			 */
			spin_lock_irqsave(&xfer->lock, flags);
			if (xfer->state == SCMI_XFER_SENT_OK) {
				info->desc->ops->fetch_response(cinfo, xfer);
				xfer->state = SCMI_XFER_RESP_OK;
			}
			spin_unlock_irqrestore(&xfer->lock, flags);
		} else {
			ret = -ETIMEDOUT;
		}
	} else {
		/* And we wait for the response. */
		timeout = msecs_to_jiffies(info->desc->max_rx_timeout_ms);
		if (!wait_for_completion_timeout(&xfer->done, timeout)) {
			dev_err(dev, "timed out in resp(caller: %pS)\n",
				(void *)_RET_IP_);
			ret = -ETIMEDOUT;
		}
	}

	ret = scmi_wait_for_message_response(cinfo, xfer);
	if (!ret && xfer->hdr.status)
		ret = scmi_to_linux_errno(xfer->hdr.status);

	if (info->desc->ops->mark_txdone)
		info->desc->ops->mark_txdone(cinfo, ret);
		info->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);
@@ -858,6 +923,20 @@ static void reset_rx_to_maxsz(const struct scmi_protocol_handle *ph,
 * @ph: Pointer to SCMI protocol handle
 * @xfer: Transfer to initiate and wait for response
 *
 * Using asynchronous commands in atomic/polling mode should be avoided since
 * it could cause long busy-waiting here, so ignore polling for the delayed
 * response and WARN if it was requested for this command transaction since
 * upper layers should refrain from issuing such kind of requests.
 *
 * The only other option would have been to refrain from using any asynchronous
 * command even if made available, when an atomic transport is detected, and
 * instead forcibly use the synchronous version (thing that can be easily
 * attained at the protocol layer), but this would also have led to longer
 * stalls of the channel for synchronous commands and possibly timeouts.
 * (in other words there is usually a good reason if a platform provides an
 *  asynchronous version of a command and we should prefer to use it...just not
 *  when using atomic/polling mode)
 *
 * Return: -ETIMEDOUT in case of no delayed response, if transmit error,
 *	return corresponding error, else if all goes well, return 0.
 */
@@ -869,13 +948,25 @@ static int do_xfer_with_response(const struct scmi_protocol_handle *ph,

	xfer->async_done = &async_response;

	/*
	 * Delayed responses should not be polled, so an async command should
	 * not have been used when requiring an atomic/poll context; WARN and
	 * perform instead a sleeping wait.
	 * (Note Async + IgnoreDelayedResponses are sent via do_xfer)
	 */
	WARN_ON_ONCE(xfer->hdr.poll_completion);

	ret = do_xfer(ph, xfer);
	if (!ret) {
		if (!wait_for_completion_timeout(xfer->async_done, timeout))
		if (!wait_for_completion_timeout(xfer->async_done, timeout)) {
			dev_err(ph->dev,
				"timed out in delayed resp(caller: %pS)\n",
				(void *)_RET_IP_);
			ret = -ETIMEDOUT;
		else if (xfer->hdr.status)
		} else if (xfer->hdr.status) {
			ret = scmi_to_linux_errno(xfer->hdr.status);
		}
	}

	xfer->async_done = NULL;
	return ret;
@@ -1308,6 +1399,22 @@ static void scmi_devm_protocol_put(struct scmi_device *sdev, u8 protocol_id)
	WARN_ON(ret);
}

/**
 * scmi_is_transport_atomic  - Method to check if underlying transport for an
 * SCMI instance is configured as atomic.
 *
 * @handle: A reference to the SCMI platform instance.
 *
 * Return: True if transport is configured as atomic
 */
static bool scmi_is_transport_atomic(const struct scmi_handle *handle)
{
	struct scmi_info *info = handle_to_scmi_info(handle);

	return info->desc->atomic_enabled &&
		is_transport_polling_capable(info);
}

static inline
struct scmi_handle *scmi_handle_get_from_info_unlocked(struct scmi_info *info)
{
@@ -1499,6 +1606,16 @@ static int scmi_chan_setup(struct scmi_info *info, struct device *dev,
	if (ret)
		return ret;

	if (tx && is_polling_required(cinfo, info)) {
		if (is_transport_polling_capable(info))
			dev_info(dev,
				 "Enabled polling mode TX channel - prot_id:%d\n",
				 prot_id);
		else
			dev_warn(dev,
				 "Polling mode NOT supported by transport.\n");
	}

idr_alloc:
	ret = idr_alloc(idr, cinfo, prot_id, prot_id + 1, GFP_KERNEL);
	if (ret != prot_id) {
@@ -1835,6 +1952,7 @@ static int scmi_probe(struct platform_device *pdev)
	handle->version = &info->version;
	handle->devm_protocol_get = scmi_devm_protocol_get;
	handle->devm_protocol_put = scmi_devm_protocol_put;
	handle->is_transport_atomic = scmi_is_transport_atomic;

	if (desc->ops->link_supplier) {
		ret = desc->ops->link_supplier(dev);
@@ -1853,6 +1971,10 @@ static int scmi_probe(struct platform_device *pdev)
	if (scmi_notification_init(handle))
		dev_err(dev, "SCMI Notifications NOT available.\n");

	if (info->desc->atomic_enabled && !is_transport_polling_capable(info))
		dev_err(dev,
			"Transport is not polling capable. Atomic mode not supported.\n");

	/*
	 * Trigger SCMI Base protocol initialization.
	 * It's mandatory and won't be ever released/deinit until the
@@ -1994,6 +2116,9 @@ static const struct of_device_id scmi_of_match[] = {
#ifdef CONFIG_ARM_SCMI_TRANSPORT_MAILBOX
	{ .compatible = "arm,scmi", .data = &scmi_mailbox_desc },
#endif
#ifdef CONFIG_ARM_SCMI_TRANSPORT_OPTEE
	{ .compatible = "linaro,scmi-optee", .data = &scmi_optee_desc },
#endif
#ifdef CONFIG_ARM_SCMI_TRANSPORT_SMC
	{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
#endif
Loading