Commit a7afae50 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

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

Merge tag 'scmi-updates-5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into arm/drivers

ARM SCMI/SCPI updates for v5.8

1. Addition of ARM SMC/HVC as SCMI transport type with required
   abstraction already in place
2. Initial infrastructure support to add SCMI notifications from
   platform to agents
3. Miscellaneous fix adding header include guards

* tag 'scmi-updates-5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: arm_scmi: fix psci dependency
  firmware: arm_scmi: Fix return error code in smc_send_message
  firmware: arm_scmi: Fix handling of unexpected delayed responses
  firmware: arm_scmi: Clear channel for delayed responses
  firmware: arm_scmi: Clear channel on reception of unexpected responses
  firmware: arm_scmi: Rename .clear_notification() transport_ops
  firmware: arm_scmi: Add support for notifications message processing
  firmware: arm_scmi: Add notifications support in transport layer
  firmware: arm_scmi: Update protocol commands and notification list
  firmware: arm_scmi: Add receive buffer support for notifications
  firmware: arm_scpi: Add include guard to linux/scpi_protocol.h
  firmware: arm_scmi: Add include guard to linux/scmi_protocol.h
  firmware: arm_scmi: Drop checking for shmem property in parent node
  firmware: arm_scmi: Check shmem property for channel availablity
  firmware: arm_scmi: Drop empty stub for smc_mark_txdone
  firmware: arm_scmi: Make mutex channel specific
  firmware: arm_scmi: Add smc/hvc transport
  dt-bindings: arm: Add smc/hvc transport for SCMI

Link: https://lore.kernel.org/r/20200512110357.GA26454@bogus


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 0e698dfa 5a897e3a
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@ Required properties:

The scmi node with the following properties shall be under the /firmware/ node.

- compatible : shall be "arm,scmi"
- compatible : shall be "arm,scmi" or "arm,scmi-smc" for smc/hvc transports
- mboxes: List of phandle and mailbox channel specifiers. It should contain
	  exactly one or two mailboxes, one for transmitting messages("tx")
	  and another optional for receiving the notifications("rx") if
@@ -25,6 +25,7 @@ The scmi node with the following properties shall be under the /firmware/ node.
	  protocol identifier for a given sub-node.
- #size-cells : should be '0' as 'reg' property doesn't have any size
	  associated with it.
- arm,smc-id : SMC id required when using smc or hvc transports

Optional properties:

+3 −1
Original line number Diff line number Diff line
@@ -2,6 +2,8 @@
obj-y	= scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o
scmi-bus-y = bus.o
scmi-driver-y = driver.o
scmi-transport-y = mailbox.o shmem.o
scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
scmi-transport-$(CONFIG_ARM_PSCI_FW) += smc.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
+7 −0
Original line number Diff line number Diff line
@@ -14,6 +14,13 @@ enum scmi_base_protocol_cmd {
	BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
	BASE_DISCOVER_AGENT = 0x7,
	BASE_NOTIFY_ERRORS = 0x8,
	BASE_SET_DEVICE_PERMISSIONS = 0x9,
	BASE_SET_PROTOCOL_PERMISSIONS = 0xa,
	BASE_RESET_AGENT_CONFIGURATION = 0xb,
};

enum scmi_base_protocol_notify {
	BASE_ERROR_EVENT = 0x0,
};

struct scmi_msg_resp_base_attributes {
+11 −0
Original line number Diff line number Diff line
@@ -178,6 +178,8 @@ struct scmi_chan_info {
 * @send_message: Callback to send a message
 * @mark_txdone: Callback to mark tx as done
 * @fetch_response: Callback to fetch response
 * @fetch_notification: Callback to fetch notification
 * @clear_channel: Callback to clear a channel
 * @poll_done: Callback to poll transfer status
 */
struct scmi_transport_ops {
@@ -190,6 +192,9 @@ struct scmi_transport_ops {
	void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret);
	void (*fetch_response)(struct scmi_chan_info *cinfo,
			       struct scmi_xfer *xfer);
	void (*fetch_notification)(struct scmi_chan_info *cinfo,
				   size_t max_len, struct scmi_xfer *xfer);
	void (*clear_channel)(struct scmi_chan_info *cinfo);
	bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer);
};

@@ -210,6 +215,9 @@ struct scmi_desc {
};

extern const struct scmi_desc scmi_mailbox_desc;
#ifdef CONFIG_HAVE_ARM_SMCCC
extern const struct scmi_desc scmi_smc_desc;
#endif

void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr);
void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
@@ -222,5 +230,8 @@ void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem);
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
			  struct scmi_xfer *xfer);
void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
			      size_t max_len, struct scmi_xfer *xfer);
void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
		     struct scmi_xfer *xfer);
+105 −28
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ struct scmi_xfers_info {
 *	implementation version and (sub-)vendor identification.
 * @handle: Instance of SCMI handle to send to clients
 * @tx_minfo: Universal Transmit Message management info
 * @rx_minfo: Universal Receive Message management info
 * @tx_idr: IDR object to map protocol id to Tx channel info pointer
 * @rx_idr: IDR object to map protocol id to Rx channel info pointer
 * @protocols_imp: List of protocols implemented, currently maximum of
@@ -89,6 +90,7 @@ struct scmi_info {
	struct scmi_revision_info version;
	struct scmi_handle handle;
	struct scmi_xfers_info tx_minfo;
	struct scmi_xfers_info rx_minfo;
	struct idr tx_idr;
	struct idr rx_idr;
	u8 *protocols_imp;
@@ -200,37 +202,66 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
}

/**
 * scmi_rx_callback() - callback for receiving messages
 *
 * @cinfo: SCMI channel info
 * @msg_hdr: Message header
 *
 * Processes one received message to appropriate transfer information and
 * signals completion of the transfer.
 *
 * NOTE: This function will be invoked in IRQ context, hence should be
 * as optimal as possible.
 */
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
{
	struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
	struct scmi_xfers_info *minfo = &info->tx_minfo;
	u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
	u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
	struct device *dev = cinfo->dev;
	struct scmi_xfer *xfer;
	struct device *dev = cinfo->dev;
	struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
	struct scmi_xfers_info *minfo = &info->rx_minfo;

	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));
		info->desc->ops->clear_channel(cinfo);
		return;
	}

	if (msg_type == MSG_TYPE_NOTIFICATION)
		return; /* Notifications not yet supported */
	unpack_scmi_header(msg_hdr, &xfer->hdr);
	scmi_dump_header_dbg(dev, &xfer->hdr);
	info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size,
					    xfer);

	trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
			   xfer->hdr.protocol_id, xfer->hdr.seq,
			   MSG_TYPE_NOTIFICATION);

	__scmi_xfer_put(minfo, xfer);

	info->desc->ops->clear_channel(cinfo);
}

static void scmi_handle_response(struct scmi_chan_info *cinfo,
				 u16 xfer_id, u8 msg_type)
{
	struct scmi_xfer *xfer;
	struct device *dev = cinfo->dev;
	struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
	struct scmi_xfers_info *minfo = &info->tx_minfo;

	/* Are we even expecting this? */
	if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
		dev_err(dev, "message for %d is not expected!\n", xfer_id);
		info->desc->ops->clear_channel(cinfo);
		return;
	}

	xfer = &minfo->xfer_block[xfer_id];
	/*
	 * Even if a response was indeed expected on this slot at this point,
	 * a buggy platform could wrongly reply feeding us an unexpected
	 * delayed response we're not prepared to handle: bail-out safely
	 * blaming firmware.
	 */
	if (unlikely(msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done)) {
		dev_err(dev,
			"Delayed Response for %d not expected! Buggy F/W ?\n",
			xfer_id);
		info->desc->ops->clear_channel(cinfo);
		/* It was unexpected, so nobody will clear the xfer if not us */
		__scmi_xfer_put(minfo, xfer);
		return;
	}

	scmi_dump_header_dbg(dev, &xfer->hdr);

@@ -240,11 +271,44 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
			   xfer->hdr.protocol_id, xfer->hdr.seq,
			   msg_type);

	if (msg_type == MSG_TYPE_DELAYED_RESP)
	if (msg_type == MSG_TYPE_DELAYED_RESP) {
		info->desc->ops->clear_channel(cinfo);
		complete(xfer->async_done);
	else
	} else {
		complete(&xfer->done);
	}
}

/**
 * scmi_rx_callback() - callback for receiving messages
 *
 * @cinfo: SCMI channel info
 * @msg_hdr: Message header
 *
 * Processes one received message to appropriate transfer information and
 * signals completion of the transfer.
 *
 * NOTE: This function will be invoked in IRQ context, hence should be
 * as optimal as possible.
 */
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
{
	u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
	u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);

	switch (msg_type) {
	case MSG_TYPE_NOTIFICATION:
		scmi_handle_notification(cinfo, msg_hdr);
		break;
	case MSG_TYPE_COMMAND:
	case MSG_TYPE_DELAYED_RESP:
		scmi_handle_response(cinfo, xfer_id, msg_type);
		break;
	default:
		WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type);
		break;
	}
}

/**
 * scmi_xfer_put() - Release a transmit message
@@ -525,13 +589,13 @@ int scmi_handle_put(const struct scmi_handle *handle)
	return 0;
}

static int scmi_xfer_info_init(struct scmi_info *sinfo)
static int __scmi_xfer_info_init(struct scmi_info *sinfo,
				 struct scmi_xfers_info *info)
{
	int i;
	struct scmi_xfer *xfer;
	struct device *dev = sinfo->dev;
	const struct scmi_desc *desc = sinfo->desc;
	struct scmi_xfers_info *info = &sinfo->tx_minfo;

	/* Pre-allocated messages, no more than what hdr.seq can support */
	if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) {
@@ -566,6 +630,16 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo)
	return 0;
}

static int scmi_xfer_info_init(struct scmi_info *sinfo)
{
	int ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo);

	if (!ret && idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE))
		ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo);

	return ret;
}

static int scmi_chan_setup(struct scmi_info *info, struct device *dev,
			   int prot_id, bool tx)
{
@@ -699,10 +773,6 @@ static int scmi_probe(struct platform_device *pdev)
	info->desc = desc;
	INIT_LIST_HEAD(&info->node);

	ret = scmi_xfer_info_init(info);
	if (ret)
		return ret;

	platform_set_drvdata(pdev, info);
	idr_init(&info->tx_idr);
	idr_init(&info->rx_idr);
@@ -715,6 +785,10 @@ static int scmi_probe(struct platform_device *pdev)
	if (ret)
		return ret;

	ret = scmi_xfer_info_init(info);
	if (ret)
		return ret;

	ret = scmi_base_protocol_init(handle);
	if (ret) {
		dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
@@ -827,6 +901,9 @@ ATTRIBUTE_GROUPS(versions);
/* Each compatible listed below must have descriptor associated with it */
static const struct of_device_id scmi_of_match[] = {
	{ .compatible = "arm,scmi", .data = &scmi_mailbox_desc },
#ifdef CONFIG_ARM_PSCI_FW
	{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
#endif
	{ /* Sentinel */ },
};

Loading